将float转换为四舍五入的十进制数 [英] Convert float to rounded decimal equivalent

查看:173
本文介绍了将float转换为四舍五入的十进制数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当您将浮动转换为十进制时,十进制将包含它可以准确表示的二进制数。准确是很好的,但并不总是你想要的。由于很多十进制数不能完全用二进制表示,因此产生的 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

我试图转换为 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆