`decimal.Decimal(1.0/3.0)` 中的 27 位额外精度从何而来?
Whence the 27 digits of extra precision in `decimal.Decimal(1.0/3.0)`?
这个post是关于表达式decimal.Decimal(1.0/3.0)
significant digits的个数decimal.Decimal(1.0/3.0)
.
decimal.Decimal
的文档说 "[t]he significance of a new Decimal is determined solely by the number of digits input"。
据此,我认为 decimal.Decimal(1.0/3.0)
中的有效数字位数应由运算 1.0/3.0
产生的 IEEE 754 double 中的有效数字位数决定。
现在,据我了解,IEEE 754 64 位双精度数具有“15-17 significant decimal digits precision”。
因此,综合以上所有内容,我预计 decimal.Decimal(1.0/3.0)
最多有 17 位有效小数位。
然而,decimal.Decimal(1.0/3.0)
似乎至少有 54 位有效的小数位:
import decimal
print decimal.Decimal(1.0/3.0)
# 0.333333333333333314829616256247390992939472198486328125
所有这一切归结为两个关键问题:
- 声称 IEEE 754 double 具有 "a 15-17 significant decimal digits precision" 的依据是什么?
- 如何解决以下各项之间的矛盾?:
- 上面引用的
decimal.Decimal
的文档
decimal.Decimal(1.0/3.0)
中的 54 位(或更多)有效数字
- IEEE 754 双精度数中有效小数位的最大值为 17。
附录: 好的,我现在对情况有了更好的了解,感谢 ajcr 的回答,以及一些补充意见。
在内部,decimal
将 1.0/3.0
表示为分数
6004799503160661/18014398509481984
这个分数的分母是254。分子为(254 - 1)/3, 正好.
这个分数的十进制表示是,正好
0.333333333333333314829616256247390992939472198486328125
附录 2: 再试一次。浮点数 F 是不可数 set 实数的替代。这组值包括有理数Q(F)也就是正好用浮点数表示编号 F。它还包括无数高于和低于 Q(F) 的实数值。现在,给定一个 64 位 IEEE 754 double 范围内的实数 R,令 F(R) 是 R 在表示为浮点数 1.
时映射到的双精度数
例如,如果R = 1/3,则F(R)是由以下 64 位给出的 IEEE 754 double:
0 01111111101 0101010101010101010101010101010101010101010101010101 = F(R)
...和Q(F(R))是分数N/D,其中 D = 254 = 18014398509481984 , 和 N = (254 - 1)/3 = 6004799503160661. 简而言之:
6004799503160661/18014398509481984 = Q(F(R))
或者,作为 精确 十进制:
0.333333333333333314829616256247390992939472198486328125 = Q(F(R))
但是浮点数F(R)不仅代表R = 1/3 和 Q(F(R)) = N/D,也是对所有的实数
在 (A, B) 2 范围内,其中 A = (2 N - 1)/2 D, B = (2 N + 1)/2 D。下面我显示 exact 十进制表示 A < Q(F(R)) < B,以及 不精确 的 54 位表示R = 1/3:
0.3333333333333332593184650249895639717578887939453125 = A
0.333333333333333314829616256247390992939472198486328125 = Q(F(R))
0.333333333333333333333333333333333333333333333333333333 ~ R
0.33333333333333337034076748750521801412105560302734375 = B
现在,这里是相同的四个数字的十进制表示 A, Q(F(R))、R和B,但现在四舍五入到 17 位有效数字:
0.33333333333333326 ~ A
0.33333333333333331 ~ Q(F(R))
0.33333333333333333 ~ R
0.33333333333333337 ~ B
这至少可以解释为什么据说 IEEE 754 双精度数具有“15-17 位有效小数位精度”。更明确地说,由给定 IEEE 754 double 表示的任何两个 real 数字的十进制表示将在其最高有效数字的 15 到 17 之间一致。
好的,回到Q(F(R))。是的,这是一个分母是2的幂的有理数,因此我们可以计算出它的小数展开正好。这个扩展包中的重要数字几乎是无限的。但是这个数在这里的作用严格来说是不可数实数集的典型代表,所有这些数最多共享 17 个有效数字。因此,要在 Q(F(R)) 量的扩展中使用更多有效数字对这组实数的错误表述。 IOW,Q(F(R)) 的十进制展开中的最低 27 位数字是, 在这个意义上, 无关紧要, 确实 不显着, 相对于 Q(F(R)) 作为 (A, B 中所有数字的替代), 包括 R.
换句话说,当它作为区间的代表时(A,B),Q(F(R)) 应该只是
0.33333333333333331 ~ Q(F(R))
其十进制展开的其余部分与此角色无关,因此,应将其隐藏起来。
我意识到,考虑到对它提出的所有要求,可能很难将设计 decimal
做得比现在更好。 IOW,实际上,上述虚假陈述可能是不可避免的。但是,至少应该清楚地记录它,以及所有其他与浮点数相关的或多或少不可避免的错误陈述。
1 对,我是维护IEEE 754 double F( R)(内存中的特定位序列),以及有理数 Q(F( R))(一个数学实体),绝对清楚。
2我想它也包括这个范围的一个端点,但是这个细节在这里并不重要。
在 'Decimal(1.0/3.0)' 中,您将浮点除法的结果(您已证明不正确)转换为十进制。你的意思是:
>>>> Decimal("1.0")/Decimal("3.0")
Decimal('0.3333333333333333333333333333')
关于浮点数的本质:
My understanding is that a 64-bit double "gives a 15-17 significant decimal digits precision".
Decimal() 的输入中没有比单个浮点数更多的信息。它不知道它是算术运算的结果;毕竟,您可以将其命名为 Decimal(0.333333333333333314829616256247390992939472198486328125)
。
这就是小数应始终由字符串或整数构造的原因:它们不会受到这种歧义的影响。
当传递一个浮点数时,Decimal 使用 from_float
构造函数。此 class 方法从单个 Python 浮点数 exactly 构造一个 Decimal;它不知道浮点数是如何计算出来的,人类可能认为它只精确到一定数量的数字。
相反,它通过将浮点数视为两个整数的比率来确定从浮点数中获取的适当位数。这是第 740 行:
n, d = abs(f).as_integer_ratio()
k = d.bit_length() - 1
result = _dec_from_triple(sign, str(n*5**k), -k)
这意味着 1.0/3.0
我们有以下内容:
>>> f = 1.0 / 3.0
>>> f.as_integer_ratio()
(6004799503160661, 18014398509481984)
>>> (18014398509481984).bit_length()
55
为了构造小数,计算符号、系数和指数并将其传递给_dec_from_triple
。在这种情况下,系数是字符串:
'333333333333333314829616256247390992939472198486328125'
指数是-(55-1)
。这给出了小数点后准确的 54 位数字,因此您的观察结果。
我认为很多混淆源于对 IEEE 754 浮点数的误解。
它不是一个区间运算系统。每个浮点数都有一个精确定义的值。每次计算的结果是从对输入的指定值应用相应的实数运算的结果开始舍入到最接近的结果。
1.0/3.0 是最接近实数除法 1.0/3.0 结果的可表示值。 It is not the result of division of the range [9999999.99999999944488848768742172978818416595458984375,10000000.0000000011102230246251565404236316680908203125] by the range [29999999.999999997779553950749686919152736663818359375,30000000.000000002220446049250313080847263336181640625].
额外的舍入步骤往往会增加舍入误差,这通常是不可取的,除非有额外的数据证明舍入是合理的。
这个post是关于表达式decimal.Decimal(1.0/3.0)
significant digits的个数decimal.Decimal(1.0/3.0)
.
decimal.Decimal
的文档说 "[t]he significance of a new Decimal is determined solely by the number of digits input"。
据此,我认为 decimal.Decimal(1.0/3.0)
中的有效数字位数应由运算 1.0/3.0
产生的 IEEE 754 double 中的有效数字位数决定。
现在,据我了解,IEEE 754 64 位双精度数具有“15-17 significant decimal digits precision”。
因此,综合以上所有内容,我预计 decimal.Decimal(1.0/3.0)
最多有 17 位有效小数位。
然而,decimal.Decimal(1.0/3.0)
似乎至少有 54 位有效的小数位:
import decimal
print decimal.Decimal(1.0/3.0)
# 0.333333333333333314829616256247390992939472198486328125
所有这一切归结为两个关键问题:
- 声称 IEEE 754 double 具有 "a 15-17 significant decimal digits precision" 的依据是什么?
- 如何解决以下各项之间的矛盾?:
- 上面引用的
decimal.Decimal
的文档 decimal.Decimal(1.0/3.0)
中的 54 位(或更多)有效数字
- IEEE 754 双精度数中有效小数位的最大值为 17。
- 上面引用的
附录: 好的,我现在对情况有了更好的了解,感谢 ajcr 的回答,以及一些补充意见。
在内部,decimal
将 1.0/3.0
表示为分数
6004799503160661/18014398509481984
这个分数的分母是254。分子为(254 - 1)/3, 正好.
这个分数的十进制表示是,正好
0.333333333333333314829616256247390992939472198486328125
附录 2: 再试一次。浮点数 F 是不可数 set 实数的替代。这组值包括有理数Q(F)也就是正好用浮点数表示编号 F。它还包括无数高于和低于 Q(F) 的实数值。现在,给定一个 64 位 IEEE 754 double 范围内的实数 R,令 F(R) 是 R 在表示为浮点数 1.
时映射到的双精度数例如,如果R = 1/3,则F(R)是由以下 64 位给出的 IEEE 754 double:
0 01111111101 0101010101010101010101010101010101010101010101010101 = F(R)
...和Q(F(R))是分数N/D,其中 D = 254 = 18014398509481984 , 和 N = (254 - 1)/3 = 6004799503160661. 简而言之:
6004799503160661/18014398509481984 = Q(F(R))
或者,作为 精确 十进制:
0.333333333333333314829616256247390992939472198486328125 = Q(F(R))
但是浮点数F(R)不仅代表R = 1/3 和 Q(F(R)) = N/D,也是对所有的实数 在 (A, B) 2 范围内,其中 A = (2 N - 1)/2 D, B = (2 N + 1)/2 D。下面我显示 exact 十进制表示 A < Q(F(R)) < B,以及 不精确 的 54 位表示R = 1/3:
0.3333333333333332593184650249895639717578887939453125 = A
0.333333333333333314829616256247390992939472198486328125 = Q(F(R))
0.333333333333333333333333333333333333333333333333333333 ~ R
0.33333333333333337034076748750521801412105560302734375 = B
现在,这里是相同的四个数字的十进制表示 A, Q(F(R))、R和B,但现在四舍五入到 17 位有效数字:
0.33333333333333326 ~ A
0.33333333333333331 ~ Q(F(R))
0.33333333333333333 ~ R
0.33333333333333337 ~ B
这至少可以解释为什么据说 IEEE 754 双精度数具有“15-17 位有效小数位精度”。更明确地说,由给定 IEEE 754 double 表示的任何两个 real 数字的十进制表示将在其最高有效数字的 15 到 17 之间一致。
好的,回到Q(F(R))。是的,这是一个分母是2的幂的有理数,因此我们可以计算出它的小数展开正好。这个扩展包中的重要数字几乎是无限的。但是这个数在这里的作用严格来说是不可数实数集的典型代表,所有这些数最多共享 17 个有效数字。因此,要在 Q(F(R)) 量的扩展中使用更多有效数字对这组实数的错误表述。 IOW,Q(F(R)) 的十进制展开中的最低 27 位数字是, 在这个意义上, 无关紧要, 确实 不显着, 相对于 Q(F(R)) 作为 (A, B 中所有数字的替代), 包括 R.
换句话说,当它作为区间的代表时(A,B),Q(F(R)) 应该只是
0.33333333333333331 ~ Q(F(R))
其十进制展开的其余部分与此角色无关,因此,应将其隐藏起来。
我意识到,考虑到对它提出的所有要求,可能很难将设计 decimal
做得比现在更好。 IOW,实际上,上述虚假陈述可能是不可避免的。但是,至少应该清楚地记录它,以及所有其他与浮点数相关的或多或少不可避免的错误陈述。
1 对,我是维护IEEE 754 double F( R)(内存中的特定位序列),以及有理数 Q(F( R))(一个数学实体),绝对清楚。
2我想它也包括这个范围的一个端点,但是这个细节在这里并不重要。
在 'Decimal(1.0/3.0)' 中,您将浮点除法的结果(您已证明不正确)转换为十进制。你的意思是:
>>>> Decimal("1.0")/Decimal("3.0")
Decimal('0.3333333333333333333333333333')
关于浮点数的本质:
My understanding is that a 64-bit double "gives a 15-17 significant decimal digits precision".
Decimal() 的输入中没有比单个浮点数更多的信息。它不知道它是算术运算的结果;毕竟,您可以将其命名为 Decimal(0.333333333333333314829616256247390992939472198486328125)
。
这就是小数应始终由字符串或整数构造的原因:它们不会受到这种歧义的影响。
当传递一个浮点数时,Decimal 使用 from_float
构造函数。此 class 方法从单个 Python 浮点数 exactly 构造一个 Decimal;它不知道浮点数是如何计算出来的,人类可能认为它只精确到一定数量的数字。
相反,它通过将浮点数视为两个整数的比率来确定从浮点数中获取的适当位数。这是第 740 行:
n, d = abs(f).as_integer_ratio()
k = d.bit_length() - 1
result = _dec_from_triple(sign, str(n*5**k), -k)
这意味着 1.0/3.0
我们有以下内容:
>>> f = 1.0 / 3.0
>>> f.as_integer_ratio()
(6004799503160661, 18014398509481984)
>>> (18014398509481984).bit_length()
55
为了构造小数,计算符号、系数和指数并将其传递给_dec_from_triple
。在这种情况下,系数是字符串:
'333333333333333314829616256247390992939472198486328125'
指数是-(55-1)
。这给出了小数点后准确的 54 位数字,因此您的观察结果。
我认为很多混淆源于对 IEEE 754 浮点数的误解。
它不是一个区间运算系统。每个浮点数都有一个精确定义的值。每次计算的结果是从对输入的指定值应用相应的实数运算的结果开始舍入到最接近的结果。
1.0/3.0 是最接近实数除法 1.0/3.0 结果的可表示值。 It is not the result of division of the range [9999999.99999999944488848768742172978818416595458984375,10000000.0000000011102230246251565404236316680908203125] by the range [29999999.999999997779553950749686919152736663818359375,30000000.000000002220446049250313080847263336181640625].
额外的舍入步骤往往会增加舍入误差,这通常是不可取的,除非有额外的数据证明舍入是合理的。