将float
转换为Decimal
时,Decimal
将尽可能准确地表示二进制数。准确的很好,但并不总是你想要的。由于许多十进制数字不能完全用二进制表示,因此得到的Decimal
会稍微偏离 - 有时会有点高,有时会有点低。
>>> from decimal import Decimal
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print Decimal(f)
0.1000000000000000055511151231257827021181583404541015625
0.299999999999999988897769753748434595763683319091796875
10000000000000000905969664
9999999999999999583119736832
1.000000000000099920072216264088638126850128173828125
理想情况下,我们希望Decimal
四舍五入到最可能的十进制等值。
我尝试转换为str
,因为从字符串创建的Decimal
将是准确的。不幸的是str
有点太过分了。
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print Decimal(str(f))
0.1
0.3
1E+25
1E+28
1.0
有没有办法从浮动中获得一个很好的舍入Decimal
?
答案 0 :(得分:4)
事实证明,repr
可以更好地将float
转换为字符串,而不是str
。这是进行转换的快捷方式。
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print Decimal(repr(f))
0.1
0.3
1E+25
1E+28
1.0000000000001
在我发现之前,我想出了一种蛮力的方法来进行四舍五入。它具有识别大数字精确到15位的优点 - 上面的repr
方法仅识别1e25和1e28示例的一个有效数字。
from decimal import Decimal,DecimalTuple
def _increment(digits, exponent):
new_digits = [0] + list(digits)
new_digits[-1] += 1
for i in range(len(new_digits)-1, 0, -1):
if new_digits[i] > 9:
new_digits[i] -= 10
new_digits[i-1] += 1
if new_digits[0]:
return tuple(new_digits[:-1]), exponent + 1
return tuple(new_digits[1:]), exponent
def nearest_decimal(f):
sign, digits, exponent = Decimal(f).as_tuple()
if len(digits) > 15:
round_up = digits[15] >= 5
exponent += len(digits) - 15
digits = digits[:15]
if round_up:
digits, exponent = _increment(digits, exponent)
while digits and digits[-1] == 0 and exponent < 0:
digits = digits[:-1]
exponent += 1
return Decimal(DecimalTuple(sign, digits, exponent))
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001):
print nearest_decimal(f)
0.1
0.3
1.00000000000000E+25
1.00000000000000E+28
1.0000000000001
编辑:我发现了使用蛮力舍入的另一个原因。 repr
尝试返回唯一标识基础float
位表示的字符串,但不一定能确保最后一位数的准确性。通过使用少一个数字,我的舍入函数将更多地是您期望的数字。
>>> print Decimal(repr(2.0/3.0))
0.6666666666666666
>>> print dec.nearest_decimal(2.0/3.0)
0.666666666666667
使用repr
创建的小数实际上更准确,但它意味着不存在的精度级别。 nearest_decimal
功能可在精度和准确度之间实现更好的匹配。
答案 1 :(得分:0)
我已经在Pharo Smalltalk中使用名为Float
的{{1}}方法实现了此功能。
与打印最小的小数部分完全相同的问题将被重新解释为相同的float / double,假设正确的舍入(到最近)。
请参阅Count number of digits after `.` in floating point numbers?的答案以获取更多参考资料