问题
Does anyone have an explanation for this strange rounding in haskell (GHCi, version 7.2.1). Everything seems fine unless I multiply with 100.
*Main> 1.1
1.1
*Main> 1.1 *10
11.0
*Main> 1.1 *100
110.00000000000001
*Main> 1.1 *1000
1100.0
*Main> 1.1 *10000
11000.0
Edit: what is puzzeling me is that the rounding error only shows when multiplying with 100.
Edit(2): The comments I received made me realize, that this it totally unrelated to haskell, but a general issue with floating point numbers. Numerous questions were already asked (and answered) about floating-point number oddities, where the undelying issue typcally was confusing floats with real numbers.
Perl, python, javascript and C all report 1.1 * 100.0 = 110.00000000000001
. Here is what C does
double 10.0 * 1.1 = 11.000000000000000000000000
double 100.0 * 1.1 = 110.000000000000014210854715
double 110.0 = 110.000000000000000000000000
double 1000.0 * 1.1 = 1100.000000000000000000000000
The question "why does this happen only when multiplying with 100" (even though there is a precise representation for 110.0) is still unanswered, but I suppose there is no simple answer, other than fully stepping through a floating-point multiplication (Thanks to Dax Fohl for stressing that 10 is nothing special in binary)
回答1:
The question "why does this happen only when multiplying with 100" (even though there is a precise representation for 110.0) is still unanswered, but I suppose there is no simple answer, other than fully stepping through a floating-point multiplication
Well, I think there may be things one can say without going to the length of writing the binary multiplication, assuming IEEE 754 arithmetic and the (default) round-to-nearest rounding mode.
The double 1.1d
is half a ULP from the real number 1.1. When you multiply it by 10, 100, 1000, and a few more powers of ten, you multiply by a number N that is exactly representable as a double, with the additional property that the result of the real multiplication 1.1 * N is exactly representable as a double, too. That makes 1.1 * N a good candidate for the result of the floating-point multiplication, which we'll write RN(N * 1.1d). But still the multiplication is not automatically rounded to 1.1 * N:
RN(N * 1.1d) = N * 1.1d + E1 with |E1| <= 0.5 * ULP(N*1.1d)
= N * (1.1 + E2) + E1 with |E2| <= 0.5 * ULP(1.1)
= N * 1.1 + (N * E2 + E1)
And the question now is how |N * E2 + E1| compares to ULP(N*1.1d), because since we have assumed N * 1.1 is exactly a floating-point number, if the result of the multiplication (which is also a floating-point number) is within 1 ULP of N * 1.1, it has to be N * 1.1.
In short, it is not so much what's special about 100… It is what's special about the real 1.1d * 100, which 1) is close to a power of two while being below it and 2) has an error of the same sign as the error when converting the real 1.1 to double.
Everytime the real N * 1.1d is relatively closer to the nearest inferior power of two than 1.1 is to 1, the result of the floating-point multiplication of 1.1d by N has to be exactly N * 1.1 (I think). An example of this case is N=1000, N*1.1d ~ 1100, just above 1024.
When the real N * 1.1d is relatively closer to the immediately superior power of two than 1.1 is to 2, there may be a floating-point number that represents N * 1.1d better than N * 1.1 does. But if the errors E1 and E2 compensate each other (i.e. have opposite signs), this should not happen.
回答2:
The number 1.1 cannot be represented in finite form in binary. It looks like 1.00011001100110011...
"Rounding errors" are just mathematically inevitable with simple floating-point arithmetic. If you want accuracy, use a Decimal number type.
http://support.microsoft.com/kb/42980
来源:https://stackoverflow.com/questions/18031221/rounding-oddity-what-is-special-about-100