当您将浮动转换为十进制时,十进制将包含它可以准确表示的二进制数。准确是很好的,但并不总是你想要的。由于很多十进制数不能完全用二进制表示,因此产生的 Decimal 会稍微偏离 - 有时会有点高,有时会有点低。
>>> from decimal import Decimal >>> f(0.1,0.3,1e25,1e28,1.0000000000001): print十进制(f) 0.1000000000000000055511151231257827021181583404541015625 0.299999999999999988897769753748434595763683319091796875 10000000000000000905969664 9999999999999999583119736832 1.000000000000099920072216264088638126850128173828125理想情况下,我们希望 Decimal $ c $
我试图转换为 str ,因为 Decimal 将是精确的。不幸的是 str 有点太过分了。
>>> ;对于f in(0.1,0.3,1e25,1e28,1.0000000000001): print十进制(str(f)) 0.1 0.3 1E + 25 1E + 28 1.0有没有办法让一个很好的四舍五入 Decimal 从一个浮点数?
解决方案事实证明, > repr 将 float 转换为比 str 更好的字符串。
>>>这是快速简单的转换方式。对于f in(0.1,0.3,1e25,1e28,1.0000000000001): print十进制(repr(f)) 0.1 0.3 1E + 25 1E + 28 1.0000000000001在我发现之前,四舍五入的方法。它具有识别大数字精确到15位的优势 - 上面的 repr 方法只能识别1e25和1e28示例的一个有效数字。
从十进制导入Decimal,DecimalTuple def _increment(数字,指数): new_digits = [0] + (len(new_digits)-1,0,-1):如果new_digits [i]>列表(数字) new_digits [-1] + = 1 9: new_digits [i] - = 10 new_digits [i-1] + = 1 如果new_digits [0]:返回元组(new_digits [: - 1]) ,指数+ 1 返回元组(new_digits [1:]),指数 最接近的十进制(f):符号,数字,指数=十进制(f).as_tuple ) len(数字)> 15: round_up = digits [15]> = 5 exponent + = len(digits) - 15 digits = digits [:15] 如果round_up:数字,指数= _增量(数字,指数)数字和数字[-1] == 0和指数< 0: digits = digits [: - 1] 指数+ = 1 返回十进制(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 位表示形式,但并不一定确保准确性最后一位数字。
>>>通过使用少一个数字,我的舍入函数更经常是您期望的数字。打印Decimal(repr(2.0 / 3.0)) 0.6666666666666666 >>> print dec.nearest_decimal(2.0 / 3.0) 0.666666666666667$ c> repr 实际上是更准确的,但它意味着不存在的精度级别。 nearest_decimal 函数在精度和精度之间提供了更好的匹配。
When you convert a float to Decimal, the Decimal will contain as accurate a representation of the binary number that it can. It's nice to be accurate, but it isn't always what you want. Since many decimal numbers can't be represented exactly in binary, the resulting Decimal will be a little off - sometimes a little high, sometimes a little low.
>>> 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.000000000000099920072216264088638126850128173828125Ideally we'd like the Decimal to be rounded to the most likely decimal equivalent.
I tried converting to str since a Decimal created from a string will be exact. Unfortunately str rounds a little too much.
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001): print Decimal(str(f)) 0.1 0.3 1E+25 1E+28 1.0Is there a way of getting a nicely rounded Decimal from a float?
解决方案It turns out that repr does a better job of converting a float to a string than str does. It's the quick-and-easy way to do the conversion.
>>> for f in (0.1, 0.3, 1e25, 1e28, 1.0000000000001): print Decimal(repr(f)) 0.1 0.3 1E+25 1E+28 1.0000000000001Before I discovered that, I came up with a brute-force way of doing the rounding. It has the advantage of recognizing that large numbers are accurate to 15 digits - the repr method above only recognizes one significant digit for the 1e25 and 1e28 examples.
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.0000000000001Edit: I discovered one more reason to use the brute-force rounding. repr tries to return a string that uniquely identifies the underlying float bit representation, but it doesn't necessarily ensure the accuracy of the last digit. By using one less digit, my rounding function will more often be the number you would expect.
>>> print Decimal(repr(2.0/3.0)) 0.6666666666666666 >>> print dec.nearest_decimal(2.0/3.0) 0.666666666666667The decimal created with repr is actually more accurate, but it implies a level of precision that doesn't exist. The nearest_decimal function delivers a better match between precision and accuracy.
更多推荐
将float转换为四舍五入的十进制数
发布评论