I develop a c++ app (Windows 7, 64 Bit, VS 2008) where the following formula is used (all vars are of type double):
mValue = floor(mValue/mStepping)*mStepping;
The idea is to shorten a number to a given count of decimal places. I think there are better ways to do this, but this is not the question here (but if you have a better option, please bring it on!).
mValue comes from user input, so in most cases the number of decimal places is already ok. But for some cases the output differs from the input.
For example mStepping has a value of 0.1 (which should round to one decimal place). Now if mValue has a value of 14.6, everything is ok. If mValue is 14.7 the result is 14.6.
So, why is floor(14.7/0.1)*0.1 = 14.6 ?
I tested other values and around 20% of them differed by 0.1. I digged further and discovered, that 14.7/0.1 has a different binary encoding than 147.0:
14.7/0.1 = ff ff ff ff ff 5f 62 40
147.0 = 00 00 00 00 00 60 62 40
I understand that the same number can be encoded differently as a double. But why does floor() handle them differently? And what can I do against it?
The usual problem: Not all exact decimal fractions can be represented exactly in binary. In your case it's 0.1 and 14.7 as well. By dividing by 0.1 you're actually not arriving at 147 but rather a minuscule amount below it:
14.699999999999999289457264239899814128875732421875
By multiplying with 0.1:
0.1000000000000000055511151231257827021181583404541
You're arriving at:
146.999999999999971578290569595992565155029296875
I think you're beginning to see the problem now, right? Flooring that number will obviously give you 146.
What you can do against it? If you want exact decimal results, then use a number type that represents decimal fractions, or a bignum library.
Oh, and a side note: No, the same number does not have different double representations. It's just that your interpretation what a number is and how it behaves is different from floating-point math.
I understand that the same number can be encoded differently as a double.
What you don't understand is that 14.7/0.1 and 147.0 are not the same number.
Write for example the following test code:
if (14.7/0.1 == 147.0)
{
cout << "We are equal!";
}
else
{
cout << "We are different!";
}
You will see that 14.7/0.1 is not equal to 147.0. It's not "the same number encoded differently", but a different number.
floor doesn't have anything to do with your surprise. floor simply returns a (possibly) different value when called with different input.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With