舍入奇怪 - “100”有什么特别之处?

时间:2013-08-03 09:12:24

标签: rounding floating-accuracy

有没有人对haskell中的这种奇怪的舍入有解释(GHCi,版本7.2.1)。除非我乘以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

编辑:令我感到困惑的是,舍入错误仅在乘以100时显示。

编辑(2):我收到的评论让我意识到,这与haskell完全无关,但是浮点数的一般问题。关于浮点数奇数的问题已经被提出(和回答)了很多问题,其中不正常的问题通常会使浮点数与实数混淆。

Perl,python,javascript和C都报告1.1 * 100.0 = 110.00000000000001。这是C做的事情

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

问题“为什么只有在乘以100时才会发生这种情况”(即使有精确的110.0表示)仍然没有答案,但我认为除了完全单步执行浮点乘法之外,没有简单的答案。 (感谢Dax Fohl强调10在二进制中没什么特别的)

2 个答案:

答案 0 :(得分:3)

数字1.1不能以二进制的有限形式表示。它看起来像1.00011001100110011 ...

“舍入错误”在数学上是不可避免的,简单floating-point arithmetic。如果需要精确度,请使用十进制数字类型。

http://support.microsoft.com/kb/42980

答案 1 :(得分:2)

  

问题“为什么只有在乘以100时才会发生这种情况”(即使有精确的110.0表示)仍然没有答案,但我认为除了完全单步执行浮点乘法之外,没有简单的答案。

嗯,我认为可能会有一些事情可以说,而不必花费二进制乘法的长度,假设IEEE 754算术和(默认)舍入到最接近的舍入模式。

1.1d是真实数字1.1的ULP的一半。当你将它乘以10,100,1000和几个10的幂时,你乘以一个完全可以表示为double的数字N,其附加属性是真实乘法1.1 * N的结果可以准确表示作为一个双倍。这使得1.1 * N成为浮点乘法结果的良好候选者,我们将编写RN(N * 1.1d)。但是乘法仍然没有自动舍入到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)

现在的问题是如何| N * E2 + E1 |与ULP(N * 1.1d)进行比较,因为我们假设N * 1.1恰好是一个浮点数,如果乘法的结果(也是一个浮点数)在N * 1.1的1 ULP范围内,它必须是N * 1.1。


简而言之,关于100的特别之处并不是那么特别......真正的1.1d * 100有什么特别之处,1)接近2的幂而低于它2)有一个错误的与将真实1.1转换为双倍时的错误相同的符号。

每当真实的N * 1.1d相对接近最接近的2的次幂时,1.1比1为1,浮点乘以1.1d的结果必须是N * 1.1(我认为) 。这种情况的一个例子是N = 1000,N * 1.1d~1100,刚好在1024以上。

当实数N * 1.1d相对接近2的立即优势2比1.1时,可能存在表示N * 1.1d优于N * 1.1的浮点数。但如果错误E1和E2相互补偿(即有相反的符号),则不应发生这种情况。