coordComplement.x = 2.9f;
blockSize = 29.0f;
coordComplement.x -= blockSize / 10.0f;
// coordComplement.x == 9.53674e-008 (WHAT?!)
coordComplement.x = 2.9f;
blockSize = 29.0f;
coordComplement.x -= 29.0f / 10.0f;
// coordComplement.x == 0.0f (OK!!)
🔒 Locked
Problems with C++ float type
Advertisement
Original Post
Hi! I'm using Visual C++ 6 (with the last Service Pack installed), and got some strange results working with floating point numbers. Just see the example bellow: Is that a rounding problem? How can I solve it without using double variables? Thanks!!
The 2 examples you posted should generate identical floating point code, so I'd check your generated program to see the differences ... also try building in debug mode and see if the results are the same (always a chance for bad optimizations) and check your compilers settings for floating point code generation.
Second, the first answer does not seem wrong when you understand floating point:
2.9f is stored as something NEAR 2.9 ... (because binary cannot store 0.1 or 0.9 perfectly (along with many other numbers) ...
so you are saying
coordComplement.x = ~2.9
blockSize = ~29
~2.9 -= (~29 / 10)
and the answer you got is like:
0.0000000953674
which is pretty darn close.
You will find that this problem is very well known and is inheirent in all floating point calculations. In addition, floating point is known to be very tricky when doing calculations on 2 large numbers that yeild a significantly smaller number (such as say 1234.5678987 - 1234.5678986) ...
Second, the first answer does not seem wrong when you understand floating point:
2.9f is stored as something NEAR 2.9 ... (because binary cannot store 0.1 or 0.9 perfectly (along with many other numbers) ...
so you are saying
coordComplement.x = ~2.9
blockSize = ~29
~2.9 -= (~29 / 10)
and the answer you got is like:
0.0000000953674
which is pretty darn close.
You will find that this problem is very well known and is inheirent in all floating point calculations. In addition, floating point is known to be very tricky when doing calculations on 2 large numbers that yeild a significantly smaller number (such as say 1234.5678987 - 1234.5678986) ...
Here is a bit of more detailed information about my problem:
Real code:
And the corresponding assembly code:
113: if ( coordComplement.x > 0.0f ) {
00402C20 mov ecx,dword ptr [ebp-4]
00402C23 fld dword ptr [ecx+24h]
00402C26 fcomp dword ptr [__real@4@4002f000000000000000 (0041f04c)]
00402C2C fnstsw ax
00402C2E test ah,41h
00402C31 jne C_PieceL::update+57h (00402c47)
114: coordComplement.x -= offset;
00402C33 mov edx,dword ptr [ebp-4]
00402C36 mov eax,dword ptr [ebp-4]
00402C39 fld dword ptr [edx+24h]
00402C3C fsub dword ptr [eax+48h]
00402C3F mov ecx,dword ptr [ebp-4]
00402C42 fstp dword ptr [ecx+24h]
115: } else {
00402C45 jmp C_PieceL::update+69h (00402c59)
116: coordComplement.x += offset;
00402C47 mov edx,dword ptr [ebp-4]
00402C4A mov eax,dword ptr [ebp-4]
00402C4D fld dword ptr [edx+24h]
00402C50 fadd dword ptr [eax+48h]
00402C53 mov ecx,dword ptr [ebp-4]
00402C56 fstp dword ptr [ecx+24h]
117: }
Can you help me even more? Is there any compiler option that correct this rounding error? Is there a simple workaround?
Thanks!!!!!
Xai: I've tried only in debug mode (no optimizations), and those values were caught by the VC++ watcher.
Real code:
// offset == blockSize * 0.1f == 2.9f;// coordComplement.x == 2.9f;if ( coordComplement.x != 0.0f ) { if ( coordComplement.x > 0.0f ) { coordComplement.x -= offset; } else { coordComplement.x += offset; }}// coordComplement.x == 2.38419e-006 instead of 0.0fAnd the corresponding assembly code:
113: if ( coordComplement.x > 0.0f ) {
00402C20 mov ecx,dword ptr [ebp-4]
00402C23 fld dword ptr [ecx+24h]
00402C26 fcomp dword ptr [__real@4@4002f000000000000000 (0041f04c)]
00402C2C fnstsw ax
00402C2E test ah,41h
00402C31 jne C_PieceL::update+57h (00402c47)
114: coordComplement.x -= offset;
00402C33 mov edx,dword ptr [ebp-4]
00402C36 mov eax,dword ptr [ebp-4]
00402C39 fld dword ptr [edx+24h]
00402C3C fsub dword ptr [eax+48h]
00402C3F mov ecx,dword ptr [ebp-4]
00402C42 fstp dword ptr [ecx+24h]
115: } else {
00402C45 jmp C_PieceL::update+69h (00402c59)
116: coordComplement.x += offset;
00402C47 mov edx,dword ptr [ebp-4]
00402C4A mov eax,dword ptr [ebp-4]
00402C4D fld dword ptr [edx+24h]
00402C50 fadd dword ptr [eax+48h]
00402C53 mov ecx,dword ptr [ebp-4]
00402C56 fstp dword ptr [ecx+24h]
117: }
Can you help me even more? Is there any compiler option that correct this rounding error? Is there a simple workaround?
Thanks!!!!!
Xai: I've tried only in debug mode (no optimizations), and those values were caught by the VC++ watcher.
Quote:
Original post by MajinMusashi
Can you help me even more? Is there any compiler option that correct this rounding error? Is there a simple workaround?
C++ FAQ Lite: [29.16] Why is floating point so inaccurate?
That's just the way floating point math works. double will make the problem less apparent by adding more accuracy, but the problem is still there, since float and double both rely on the exact same principles.
You could always manually round your numbers to the nearest whatever-decimal place; for example, this would round to the nearest 0.001:
n = floor(n * 1000.0f) / 1000.0f; Used in the right situations, this can help to eliminate the inaccurate results you might get. But this will actually hurt your accuracy if you don't actually plan on getting "round" numbers in base 10.For straight up math, ignore the inaccuracies. For comparisons of equality, don't compare for exact equality, but make sure that the values are very close to each other. For displaying, round the numbers. That's about the best advice I can give you. (Unless highly accurate numbers are an absolute necessity. In that case, find a BigNum library, such as GMP. But it isn't going to be nearly as fast as float or double math.)
What Every Computer Scientist Should Know About Floating-Point Arithmetic
The text is no longer than it needs to be, floating point numbers are that complicated and unintuitive.
[Edited by - Trap on February 24, 2005 11:18:02 AM]
The text is no longer than it needs to be, floating point numbers are that complicated and unintuitive.
[Edited by - Trap on February 24, 2005 11:18:02 AM]
2.9 and -2.9 are completely different numbers (one isn't the other with just a '-' in front of it). When you try to approximate the values using floats, the closest approximation available from 32 bits will be different for the positive and the negative number. 2.9 might be approximated as 2.89475234 and -2.9 might be approximated as -2.90123873 (numbers pulled out of nowhere, but you get the idea).
Doing something as simple as:
and you're not guaranteed that z will = 0.0
Now, if we had computers that weren't binary but decimary and worked on powers of 10... it would work. Or, if you used binary numbers for everything, there's no approximation, so you'll always get the answer you expect. The short of it all is there's no way around the "fuzziness" of floats/doubles.
Doing something as simple as:
x = 2.9;y = -2.9;z = x + y;and you're not guaranteed that z will = 0.0
Now, if we had computers that weren't binary but decimary and worked on powers of 10... it would work. Or, if you used binary numbers for everything, there's no approximation, so you'll always get the answer you expect. The short of it all is there's no way around the "fuzziness" of floats/doubles.
Floating point numbers aren't accurate. Either live with it or use something else. If you decide to live with it, then just be aware that all your results are approximations, and that not all decimal numbers can be represented nicely in binary or vica versa. 2.38419e-006 is 0.00000238419, which is pretty close to the correct answer, and is probably the best you can hope in most cases.
9.53674e-008 = 0.0000000953674
2.38419e-006 = 0.00000238419
Those are pretty close to zero.
Your problem is that you expect exact results from floating-point operations. You don't get exact results using floating point. Not even doubles will give you exact results.
2.38419e-006 = 0.00000238419
Those are pretty close to zero.
Your problem is that you expect exact results from floating-point operations. You don't get exact results using floating point. Not even doubles will give you exact results.
For the record, the reason your second version ( CoordComplement.x -= 29.0f / 10.0f; ) is more accurate is because of the optimizer. The optimizer just replaces that with -= 2.9f. Furthermore, if one of the operands had been a variable, the optimizer can still work on the other value. Your FPU does all it's operations in an 80-bit format, regardless of wether the result is stored in a float or double. The compiler can stick the hardcoded value directly into an 80 bit register, so it looses less precision than it would by first being stored as a float.
Hi all!
The location sent by Agony gave me good and interesting details about floating point numbers, and the solution that is more suitable to my needs is:
Thanks for all your help!
Goodbye!!!
The location sent by Agony gave me good and interesting details about floating point numbers, and the solution that is more suitable to my needs is:
inline bool isEqual( double x, double y ) { const double epsilon = 0.00001; return abs( x - y ) <= epsilon * abs( x );}if ( !isEqual( coordComplement.x, 0.0f ) ) { if ( coordComplement.x < 0.0f ) { coordComplement.x += offset; } else { coordComplement.x -= offset; }}Thanks for all your help!
Goodbye!!!
Quote:Bollucks! The only difference between the representation of -2.9 and +2.9 is that the sign bit is different. The exponent and mantissa parts will be identical.
Original post by Mantear
2.9 and -2.9 are completely different numbers (one isn't the other with just a '-' in front of it). When you try to approximate the values using floats, the closest approximation available from 32 bits will be different for the positive and the negative number. 2.9 might be approximated as 2.89475234 and -2.9 might be approximated as -2.90123873 (numbers pulled out of nowhere, but you get the idea).
Doing something as simple as:x = 2.9;y = -2.9;z = x + y;
and you're not guaranteed that z will = 0.0
Now, if we had computers that weren't binary but decimary and worked on powers of 10... it would work. Or, if you used binary numbers for everything, there's no approximation, so you'll always get the answer you expect. The short of it all is there's no way around the "fuzziness" of floats/doubles.
Quote:
Original post by MajinMusashi
Is that a rounding problem?
Yes.
Quote:
How can I solve it without using double variables?
A very generalized question - it depends on the problem. More info?
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement