将float转换为四舍五入的十进制数 [英] Convert float to rounded decimal equivalent
问题描述
当您将浮动
转换为十进制
时,十进制
将包含它可以准确表示的二进制数。准确是很好的,但并不总是你想要的。由于很多十进制数不能完全用二进制表示,因此产生的 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
,因为<$ c从字符串创建的$ c> 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.000000000000099920072216264088638126850128173828125
Ideally 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.0
Is 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.0000000000001
Before 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.0000000000001
Edit: 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.666666666666667
The 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转换为四舍五入的十进制数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!