提问人:GideonBear 提问时间:6/25/2022 最后编辑:Mark DickinsonGideonBear 更新时间:6/28/2022 访问量:307
Python 3 十进制模块除法仍有浮点错误
Python 3 decimal module division still has floating point errors
问:
在 Python 中使用 decimal 模块时,除法似乎仍然给出不准确的结果。这是我用来检查的代码,是我做错了什么,还是不可能用十进制模块准确地进行除法?在这种情况下,我还能用其他东西来精确地进行这种计算吗?
from decimal import Decimal as D
a = D('39.37007874')
b = D('0.0254')
c = D('1')
print(c / a) #0.02540000000010160000000040640 unexpected
print(c / b) #39.37007874015748031496062992 unexpected
print(c * b) #0.0254 expected
print(c * a) #39.37007874 expected
答:
-1赞
d r
6/25/2022
#1
您可以简单地使用圆形函数并获取所需的任意数量的小数。
from decimal import Decimal as D
a = D('39.37007874')
b = D('0.0254')
c = D('1')
print(round(c / a, 8)) #0.02540000
print(round(c / b, 8)) #39.37007874
print(round(c * b, 8)) #0.02540000
print(round(c * a, 8)) #39.37007874
评论
0赞
GideonBear
6/25/2022
有没有办法在不使用回合的情况下收到这个答案?我正在尝试使我的脚本尽可能准确。
1赞
Sören
6/25/2022
这两句话相互矛盾。0.025400000000010160000000040640 比 0.02540000 更准确,但出于某种原因您不希望这样。
0赞
d r
6/25/2022
正如 beeing 所说,您以这种方式选择精度是 2、6、10、20 ......小数。最准确的是代码中的那个。这完全取决于你以后要如何处理这些数字。以后,您可以随时根据脚本的逻辑和您的需要来格式化它们。也许你不需要在它们创建的时候对结果做任何事情。它们又好又准确。如果它们太准确,总会有圆形函数或某种格式或转换,任何适合您的东西。问候。。。
0赞
d r
6/25/2022
附言如果您愿意,这些数字中的每一个都可以有自己的小数位数。
-1赞
Monad
6/25/2022
#2
您可能希望使用 .fractions
from fractions import Fraction
a = Fraction('39.37007874')
b = Fraction('0.0254')
c = Fraction(1)
print(c / a) # 50000000/1968503937
print(c / b) # 5000/127
print(c * b) # 127/5000
print(c * a) # 1968503937/50000000
这将使除法尽可能准确,尽管其他操作(如 或)仍将与 一样准确。sqrt()
sin()
float
评论
0赞
GideonBear
6/25/2022
这确实使它尽可能准确,但在转换为浮点数时仍然给出相同的结果。问题是,我的输入数字不准确。
0赞
Diego Veralli
6/25/2022
#3
您可以通过设置全局上下文(用于中缀操作,如 )或使用实例执行操作来提高准确性:c / a
Context
>>> from decimal import Decimal, Context, getcontext
>>> Decimal('1') / Decimal('0.0254')
Decimal('39.37007874015748031496062992')
>>> getcontext().prec
28
>>> getcontext().prec = 56
>>> Decimal('1') / Decimal('0.0254')
Decimal('39.370078740157480314960629921259842519685039370078740157')
>>> ctx = Context(prec=28)
>>> ctx.divide(Decimal('1'), Decimal('0.0254'))
Decimal('39.37007874015748031496062992')
>>> ctx = Context(prec=56)
>>> ctx.divide(Decimal('1'), Decimal('0.0254'))
Decimal('39.370078740157480314960629921259842519685039370078740157')
正如其他人所指出的,你对结果的期望可能是不正确的:39.37007874 * 0.0254 != 1
>>> from decimal import Decimal, Context
>>> ctx = Context(prec=1000000)
>>> ctx.multiply(Decimal('0.0254'), Decimal('39.37007874'))
Decimal('0.999999999996')
评论
0赞
ekhumoro
6/25/2022
这些都没有真正帮助,因为(如问题的评论中所述),OP 实际上是在尝试进行公制/英寸转换。因此,即使将英寸定义为 0.0254 米,也无法计算出一米中同样精确的英寸数。39.37007874英寸的数字只是一个近似值,因此根本不应将其用作计算的基础(假设最佳精度至关重要)。
0赞
Diego Veralli
6/25/2022
我忘记了 39.37007874,只用 0.0254 运算,我们可以根据需要使用十进制精度来调整结果的精度。这一点尤其重要,因为这会导致非终止小数,因此在转换为英寸时,我们会达到许多常见十进制值的精度限制。1 / 0.0254
0赞
ekhumoro
6/25/2022
是的,这正是我说的。这两个数字的精度不同,因此不应在同一计算中使用它们。您的答案仅使用准确率最低的数字,因此更改十进制上下文的精度具有误导性和错误性(即精度 != 准确性)。
0赞
Diego Veralli
6/25/2022
这是一个公平的观点,我将更新答案以使用 0.0254。我从未使用过英制系统,所以这两个数字都不是特别熟悉,我只是用两个数字中的一个来说明精度可以调整的想法。
0赞
ekhumoro
6/25/2022
我看到你的编辑,但它应该是“提高准确性”,而不是“精度”。我还认为值得指出的是,没有简洁的方法来定义一米的英寸数。我的老木工老师曾经说过一米是“三英尺三英寸八分之三”,相当于 39.375 英寸。显然可以比这更准确,但没有简单的分数可以给出真正精确的转换。因此,OP问题的答案是,他们应该将所有计算建立在25.4mm的基础上,然后将最终结果四舍五入到适当的精度。
评论
a = float('39.37007874') b = float('0.0254') c = float('1')
a = 39.37007874 b = 0.0254 c = 1