这里的舍入错误的本质是什么?

时间:2013-02-25 03:15:30

标签: python floating-point

有人可以帮我解决一下究竟发生了什么事吗?

>>> 1e16 + 1.
1e+16
>>> 1e16 + 1.1
1.0000000000000002e+16

我使用的是64位Python 2.7。对于第一个,我会假设因为浮点数只有15的精度,所以它只是舍入错误。真正的浮点答案可能类似于

10000000000000000.999999....

小数刚刚被削减。但第二个结果让我质疑这种理解,不能完全代表?有什么想法吗?

[修改:只是澄清一下。我并不以任何方式暗示答案是“错误的”。显然,他们是对的,因为他们是。我只是想了解原因。]

4 个答案:

答案 0 :(得分:9)

它尽可能接近四舍五入。

浮动十六进制中的1e16是0x4341c37937e08000

1e16 + 2是0x4341c37937e08001

在此级别,您可以表示的最小精度差异为2.正好向下舍入1.0(因为通常IEEE浮点数学将舍入为偶数)。添加大于1.0的值将四舍五入到下一个可表示的值。

答案 1 :(得分:5)

10 ^ 16 = 0x002386f26fc10000可以精确表示为双精度浮点数。下一个可表示的数字是1e16 + 2。 1e16 + 1正确舍入到1e16,1e16 + 1.1正确舍入到1e16 + 2。检查此C程序的输出:

#include <stdio.h>
#include <math.h>
#include <stdint.h>

int main()
{
  uint64_t i = 10000000000000000ULL;
  double a = (double)i;
  double b = nextafter(a,1.0e20); // next representable number
  printf("I=0x%016llx\n",i); // 10^16 in hex
  printf("A=%a (%.4f)\n",a,a); // double representation
  printf("B=%a (%.4f)\n",b,b); // next double
}

输出:

I=0x002386f26fc10000
A=0x1.1c37937e08p+53 (10000000000000000.0000)
B=0x1.1c37937e08001p+53 (10000000000000002.0000)

答案 2 :(得分:3)

让我们解码一些花车,看看实际发生了什么!我将使用Common Lisp,它有一个方便的函数来获取有效数字(a.k.a尾数)和浮点数的指数,而不需要旋转任何位。所有使用的浮点数都是IEEE双精度浮点数。

> (integer-decode-float 1.0d0)
4503599627370496
-52
1

也就是说,如果我们将存在于有效数字中的值视为整数,那么它的最大功率为2(4503599627370496 = 2 ^ 52),按比例缩小(2 ^ -52) 。 (它没有存储为1,指数为0,因为有效数据更容易在左边没有零,这使我们可以跳过表示最左边的1位并且具有更高的精度。不是这种形式的数字被称为反规范。)

让我们看看1e16。

> (integer-decode-float 1d16)
5000000000000000
1
1

这里我们有代表(5000000000000000)* 2 ^ 1。请注意,有效数字,尽管是一个很好的圆十进制数,但不是2的幂;这是因为1e16不是2的幂。每次乘以10,你乘以2和5;乘以2只是递增指数,但乘以5是“实际”乘法,这里我们乘以5 16次。

5000000000000000 = 10001110000110111100100110111111000001000000000000000 (base 2)

注意这是一个53位二进制数,因为它应该是双浮点数具有53位有效数字。

但理解这种情况的关键是指数是1.(指数很小表示我们接近精度极限。)这意味着浮点值是2 ^ 1 = 2倍这个意义。

现在,当我们尝试表示为此数字添加1时会发生什么?好吧,我们需要以相同的比例代表1。但是我们在这个数字中可以做出的最小改变恰好是2,因为有效数字的最低有效位值为2!

也就是说,如果我们递增有效数字,进行最小可能的更改,我们得到

5000000000000001 = 10001110000110111100100110111111000001000000000000001 (base 2)

当我们应用指数时,我们得到2 * 5000000000000001 = 10000000000000002,这正是您观察到的值。您只能拥有10000000000000000或10000000000000002,而10000000000000001.1更接近后者。

(请注意,这里的问题甚至不是十进制数字不是二进制数!这里没有二进制“重复小数”,并且有效数据的右端有大量的0位 - 这只是你的输入整齐地落在最低位之外。)

答案 3 :(得分:3)

使用numpy,您可以看到下一个更大和更小的可表示的IEEE浮点数:

>>> import numpy as np
>>> huge=1e100
>>> tiny=1e-100
>>> np.nextafter(1e16,huge)
10000000000000002.0
>>> np.nextafter(1e16,tiny)
9999999999999998.0

所以:

>>> (np.nextafter(1e16,huge)-np.nextafter(1e16,tiny))/2.0
2.0

并且:

>>> 1.1>2.0/2
True

因此1e16 + 1.1正确舍入到下一个更大的IEEE可表示数字10000000000000002.0

原样:

>>> 1e16+1.0000000000000005
1.0000000000000002e+16

和1e16-(略大于1的东西)向下舍入2到下一个较小的IEEE编号:

>>> 1e16-1.0000000000000005
9999999999999998.0

请记住,32位与64位Python无关。重要的是IEEE format使用的大小。还要记住,数字的幅度越大,epsilon值(基本上两个更大和更小的IEEE值之间的差值)changes

您也可以看到这一点:

>>> def f_to_bits(f): return struct.unpack('<Q', struct.pack('<d', f))[0]
... 
>>> def bits_to_f(bits): return struct.unpack('<d', struct.pack('<Q', bits))[0]
... 
>>> bits_to_f(f_to_bits(1e16)+1)
1.0000000000000002e+16
>>> bits_to_f(f_to_bits(1e16)-1)
9999999999999998.0