Weird behaving floats/doubles

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
YDeeps1
Member
Member
Posts: 69
Joined: Tue Aug 31, 2021 7:25 am
Discord: speedy.dev
Contact:

Weird behaving floats/doubles

Post by YDeeps1 »

For some reason when doing maths on floats/doubles they start behaving rather weirdly.
For instance, when multiplying 0.235 by 1000, the result is 234, not 235.

I also wrote a script that simply to convert a fraction to an integer, it first extracts the decimal points `(x * 1e17 - (uint32_t(x)) * 1e17) / 1e17` then keeps multiplying the number by 10 until the condition dec == 0 equals to true.
Problem is with many points (can't pinpoint which ones) for example 0.295, everything freezes which I presume is due to the condition never evaluating to true. Could it be an issue with the FPU configuration?

The initiation of the FPU is pretty much the exact from the documentation and set to word 0x37F with the CR4 or'd with 0x200.

I am running in protected mode.

Thanks!
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: Weird behaving floats/doubles

Post by Octocontrabass »

YDeeps1 wrote:everything freezes which I presume is due to the condition never evaluating to true.
Have you checked with a debugger?
YDeeps1 wrote:The initiation of the FPU is pretty much the exact from the documentation and set to word 0x37F with the CR4 or'd with 0x200.
Which documentation? Which word is set to 0x37F? Why aren't you setting OSXMMEXCPT in CR4?

Which floating-point instruction sets did you tell your compiler to use?
YDeeps1
Member
Member
Posts: 69
Joined: Tue Aug 31, 2021 7:25 am
Discord: speedy.dev
Contact:

Re: Weird behaving floats/doubles

Post by YDeeps1 »

Octocontrabass wrote:
YDeeps1 wrote:everything freezes which I presume is due to the condition never evaluating to true.
Have you checked with a debugger?
YDeeps1 wrote:The initiation of the FPU is pretty much the exact from the documentation and set to word 0x37F with the CR4 or'd with 0x200.
Which documentation? Which word is set to 0x37F? Why aren't you setting OSXMMEXCPT in CR4?

Which floating-point instruction sets did you tell your compiler to use?
Have you checked with a debugger?:
I have checked and did confirm that is the case. The three digits did become integers (with the weird precision) followed by random numbers causing it to never finish and eventually overflow to a negative value.

Which documentation?:
I got the documentation confused with another one, ignore that.

Which word is set to 0x37F?:
I set the control word with fldcw to a memory location containing 0x37F.

Why aren't you setting OSXMMEXCPT in CR4?:
I have set it now (10th bit starting at 0 in CR4).

Which floating-point instruction sets did you tell your compiler to use?:
Not sure how or where to set this. I assume the defaults in a g++ cross-compiler?
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: Weird behaving floats/doubles

Post by Octocontrabass »

YDeeps1 wrote:I have checked and did confirm that is the case. The three digits did become integers (with the weird precision) followed by random numbers causing it to never finish and eventually overflow to a negative value.
It sounds like the algorithm you've chosen isn't stable. Stable algorithms take a bit of effort, but they do exist. Of course, you must still initialize the floating point environment correctly for these algorithms to work as expected.
YDeeps1 wrote:I set the control word with fldcw to a memory location containing 0x37F.
Did you initialize the rest of the x87 registers using FNINIT? Did you initialize MXCSR?
YDeeps1 wrote:Not sure how or where to set this. I assume the defaults in a g++ cross-compiler?
The defaults might depend on how you built your cross-compiler. Here are the options to control which instructions are used.
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Weird behaving floats/doubles

Post by nullplan »

YDeeps1 wrote:For instance, when multiplying 0.235 by 1000, the result is 234, not 235.
Are you rounding the result correctly or are you just truncating it?
235/1000 = 47/200. Since even the completely reduced fraction does not have a power of two as denominator, 47/200 cannot be represented as a finite binary fraction. Therefore any floating-point representation of it must be rounded, so either a little above or a little below. And indeed this can depend on the precision used to save the number (e.g. 0.1 as a single precision number was a bit below, and as a double it was slightly above 0.1). So if you save the number in a precision that is slightly below the target, then your calculation may result in 234.999... and if you then get the result by truncating instead of rounding, yes, you get 234 out of it.
Carpe diem!
YDeeps1
Member
Member
Posts: 69
Joined: Tue Aug 31, 2021 7:25 am
Discord: speedy.dev
Contact:

Re: Weird behaving floats/doubles

Post by YDeeps1 »

nullplan wrote:
YDeeps1 wrote:For instance, when multiplying 0.235 by 1000, the result is 234, not 235.
Are you rounding the result correctly or are you just truncating it?
235/1000 = 47/200. Since even the completely reduced fraction does not have a power of two as denominator, 47/200 cannot be represented as a finite binary fraction. Therefore any floating-point representation of it must be rounded, so either a little above or a little below. And indeed this can depend on the precision used to save the number (e.g. 0.1 as a single precision number was a bit below, and as a double it was slightly above 0.1). So if you save the number in a precision that is slightly below the target, then your calculation may result in 234.999... and if you then get the result by truncating instead of rounding, yes, you get 234 out of it.
That would actually make a lot of sense. The algorithm does not try to round at all and was made without real thought. I should add a precision limit first and then implement an algorithm to round nasty numbers or round to x decimal places. If I remember correctly, the numbers after were .99999 or something similar which would make sense.
YDeeps1
Member
Member
Posts: 69
Joined: Tue Aug 31, 2021 7:25 am
Discord: speedy.dev
Contact:

Re: Weird behaving floats/doubles

Post by YDeeps1 »

Octocontrabass wrote:
YDeeps1 wrote:I have checked and did confirm that is the case. The three digits did become integers (with the weird precision) followed by random numbers causing it to never finish and eventually overflow to a negative value.
It sounds like the algorithm you've chosen isn't stable. Stable algorithms take a bit of effort, but they do exist. Of course, you must still initialize the floating point environment correctly for these algorithms to work as expected.
YDeeps1 wrote:I set the control word with fldcw to a memory location containing 0x37F.
Did you initialize the rest of the x87 registers using FNINIT? Did you initialize MXCSR?
YDeeps1 wrote:Not sure how or where to set this. I assume the defaults in a g++ cross-compiler?
The defaults might depend on how you built your cross-compiler. Here are the options to control which instructions are used.
Yeah I figured my algorithm is not stable and does not take into account lack of precision.

Did you initialize the rest of the x87 registers using FNINIT? Did you initialize MXCSR?:
I thought running fninit initialized all of the registers. Regarding MXCSR I have not, first time hearing about it actually.
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: Weird behaving floats/doubles

Post by Octocontrabass »

YDeeps1 wrote:I thought running fninit initialized all of the registers. Regarding MXCSR I have not, first time hearing about it actually.
FNINIT only initializes the x87 registers. MXCSR isn't an x87 register, so you need to initialize it separately if you want to use SSE or AVX. (You don't need to initialize MXCSR if you're not using SSE/AVX.)
Post Reply