关于python:将浮点限制为两个小数点

Limiting floats to two decimal points

我要把EDOCX1[0]四舍五入到13.95。

1
2
3
4
>>> a
13.949999999999999
>>> round(a, 2)
13.949999999999999

round函数的工作方式与我预期的不一样。


您遇到了浮点数的旧问题,所有的数字都无法表示。命令行只是显示内存中完整的浮点形式。

在浮点中,四舍五入的版本是相同的数字。由于计算机是二进制的,它们将浮点数存储为整数,然后除以2的幂,因此13.95将以类似于125650429603636838/(2**53)的方式表示。

双精度数的精度为53位(16位),常规浮点数的精度为24位(8位)。python中的浮点使用双精度存储值。

例如,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  >>> 125650429603636838/(2**53)
  13.949999999999999

  >>> 234042163/(2**24)
  13.949999988079071

  >>> a=13.946
  >>> print(a)
  13.946
  >>> print("%.2f" % a)
  13.95
  >>> round(a,2)
  13.949999999999999
  >>> print("%.2f" % round(a,2))
  13.95
  >>> print("{0:.2f}".format(a))
  13.95
  >>> print("{0:.2f}".format(round(a,2)))
  13.95
  >>> print("{0:.15f}".format(round(a,2)))
  13.949999999999999

如果你只在两个小数点后作为货币,那么你有两个更好的选择:1)使用整数,以美分而不是美元存储值,然后除以100转换成美元。2)或使用小数等固定点号。


有新的格式规范,字符串格式规范迷你语言:

您可以这样做:

1
"{0:.2f}".format(13.949999999999999)

注意,上面返回的是一个字符串。为了获得浮点数,只需用float(...)包裹:

1
float("{0:.2f}".format(13.949999999999999))

注意,用EDOCX1[2]包装不会改变任何东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> x = 13.949999999999999999
>>> x
13.95
>>> g = float("{0:.2f}".format(x))
>>> g
13.95
>>> x == g
True
>>> h = round(x, 2)
>>> h
13.95
>>> x == h
True


内置的round()在Python2.7或更高版本中工作得很好。

例子:

1
2
>>> round(14.22222223, 2)
14.22

查看文档。


我觉得最简单的方法是使用format()函数。

例如:

1
2
3
4
a = 13.949999999999999
format(a, '.2f')

13.95

这将产生一个浮点数作为一个四舍五入为两个小数点的字符串。


大多数数字不能精确地用浮点数表示。如果您想对数字进行四舍五入,因为这正是您的数学公式或算法所要求的,那么您需要使用四舍五入。如果您只想将显示限制到一定的精度,那么甚至不要使用圆形,而是将其格式化为该字符串。(如果您想用其他舍入方法显示它,并且有吨,那么您需要混合这两种方法。)

1
2
3
4
>>>"%.2f" % 3.14159
'3.14'
>>>"%.2f" % 13.9499999
'13.95'

最后,也许最重要的是,如果你想要精确的数学,那么你根本不想要浮点数。通常的例子是处理货币并将"美分"存储为整数。


使用

1
print"{:.2f}".format(a)

而不是

1
print"{0:.2f}".format(a)

因为当尝试输出多个变量时,后者可能会导致输出错误(参见注释)。


请尝试以下代码:

1
2
3
4
>>> a = 0.99334
>>> a = int((a * 100) + 0.5) / 100.0 # Adding 0.5 rounds it up
>>> print a
0.99


对于python<3(例如2.6或2.7),有两种方法可以做到这一点。

1
2
3
4
5
# Option one
older_method_string ="%.9f" % numvar

# Option two (note ':' before the '.9f')
newer_method_string ="{:.9f}".format(numvar)

但请注意,对于3以上的Python版本(例如3.2或3.3),首选选项2。

有关选项2的更多信息,我建议使用Python文档中关于字符串格式的链接。

关于选项1的更多信息,这个链接就足够了,并且有关于各种标志的信息。

引用:将浮点数转换为某个精度,然后复制到字符串


您可以修改输出格式:

1
2
3
4
5
>>> a = 13.95
>>> a
13.949999999999999
>>> print"%.2f" % a
13.95


TLDR;

python 2.7.0和3.1最终解决了输入输出的舍入问题。

正确的四舍五入数字可以来回转换:str -> float() -> repr() -> float() ...Decimal -> float -> str -> Decimal。十进制类型不再是存储所必需的。

(当然,可能需要对整数的加或减结果进行四舍五入,以消除累积的最后一位错误。一个明确的十进制算法仍然很方便,但是如果不需要极端的精度或极端的连续算术运算,由str()转换成字符串(即舍入到12个有效数字)通常就足够好了。

无限测试:

1
2
3
4
5
6
7
8
9
10
import random
from decimal import Decimal
for x in iter(random.random, None):           # Verify FOREVER that rounding is fixed :-)
    assert float(repr(x)) == x                # Reversible repr() conversion.
    assert float(Decimal(repr(x))) == x
    assert len(repr(round(x, 10))) <= 12      # Smart decimal places in repr() after round.
    if x >= 0.1:                              # Implicit rounding to 12 significant digits
        assert str(x) == repr(round(x, 12))   # by str() is good enough for small errors.
        y = 1000 * x                             # Decimal type is excessive for shopping
        assert str(y) == repr(round(y, 12 - 3))  # in a supermaket with Python 2.7+ :-)

文档

参见发行说明python 2.7-其他语言更改第四段:

Conversions between floating-point numbers and strings are now correctly rounded on most platforms. These conversions occur in many different places: str() on floats and complex numbers; the float and complex constructors; numeric formatting; serializing and de-serializing floats and complex numbers using the marshal, pickle and json modules; parsing of float and imaginary literals in Python code; and Decimal-to-float conversion.

Related to this, the repr() of a floating-point number x now returns a result based on the shortest decimal string that’s guaranteed to round back to x under correct rounding (with round-half-to-even rounding mode). Previously it gave a string based on rounding x to 17 decimal digits.

相关问题

更多信息:在python 2.7之前,float的格式与当前的numpy.float64类似。两种类型都使用相同的64位IEEE754双精度和52位尾数。一个很大的区别是,np.float64.__repr__的格式经常使用过多的十进制数,这样就不会丢失任何位,但在13.949999999999和13.950000000000001之间不存在有效的IEEE 754号。结果是不好的,转换repr(float(number_as_string))是不可逆的numpy。另一方面:float.__repr__的格式使每个数字都很重要;序列没有间隙,转换是可逆的。简单地说:如果您可能有一个numpy.float64数字,请将其转换为普通的float,以便为人类格式化,而不是为数字处理器格式化,否则就不需要使用python 2.7+进行更多的格式化。


在Python 2.7中:

1
2
3
a = 13.949999999999999
output = float("%0.2f"%a)
print output


这里似乎还没有人提到它,所以让我举一个用python 3.6的f-string/template-string格式的例子,我认为这个格式非常简洁:

1
>>> f'{a:.2f}'

它也适用于较长的示例,也适用于运算符,不需要parens:

1
>>> print(f'Completed in {time.time() - start:.2f}s')

Python教程有一个称为浮点算术的附录:问题和限制。读它。它解释了正在发生的事情以及为什么python正在尽其所能。它甚至还有一个与您的相匹配的例子。让我引用一点:

1
2
>>> 0.1
0.10000000000000001

you may be tempted to use the round()
function to chop it back to the single
digit you expect. But that makes no
difference:

1
2
>>> round(0.1, 1)
0.10000000000000001

The problem is that the binary
floating-point value stored for "0.1"
was already the best possible binary
approximation to 1/10, so trying to
round it again can’t make it better:
it was already as good as it gets.

Another consequence is that since 0.1
is not exactly 1/10, summing ten
values of 0.1 may not yield exactly
1.0, either:

1
2
3
4
5
6
>>> sum = 0.0
>>> for i in range(10):
...     sum += 0.1
...
>>> sum
0.99999999999999989

解决问题的另一种方法是使用decimal模块。


它做的正是你告诉它做的,并且工作正常。请阅读有关浮点混淆的更多信息,并尝试使用十进制对象。


正如@matt指出的那样,python 3.6提供了F字符串,它们还可以使用嵌套参数:

1
2
3
4
5
value = 2.34558
precision = 2
width = 4

print(f'result: {value:{width}.{precision}f}')

显示result: 2.35


在python中,可以使用格式运算符将值四舍五入到小数点后2位:

1
print(format(14.4499923, '.2f')) // output is 14.45


为了修复类型动态语言(如python和javascript)中的浮点,我使用这种技术

1
2
3
4
5
6
7
8
9
# For example:
a = 70000
b = 0.14
c = a * b

print c # Prints 980.0000000002
# Try to fix
c = int(c * 10000)/100000
print c # Prints 980

您还可以使用十进制,如下所示:

1
2
3
4
5
6
7
8
from decimal import *
getcontext().prec = 6
Decimal(1) / Decimal(7)
# Results in 6 precision -> Decimal('0.142857')

getcontext().prec = 28
Decimal(1) / Decimal(7)
# Results in 28 precision -> Decimal('0.1428571428571428571428571429')


1
orig_float = 232569 / 16000.0

14.5355625

1
short_float = float("{:.2f}".format(orig_float))

14.54


1
2
3
4
5
6
7
8
9
from decimal import Decimal


def round_float(v, ndigits=2, rt_str=False):
    d = Decimal(v)
    v_str = ("{0:.%sf}" % ndigits).format(round(d, ndigits))
    if rt_str:
        return v_str
    return Decimal(v_str)

结果:

1
2
3
4
5
6
7
8
9
10
11
Python 3.6.1 (default, Dec 11 2018, 17:41:10)
>>> round_float(3.1415926)
Decimal('3.14')
>>> round_float(3.1445926)
Decimal('3.14')
>>> round_float(3.1455926)
Decimal('3.15')
>>> round_float(3.1455926, rt_str=True)
'3.15'
>>> str(round_float(3.1455926))
'3.15'


它很简单,比如1,2,3:

  • 使用十进制模块快速正确舍入十进制浮点运算:

    D=小数(10000000.0000009)

  • 要实现舍入:

    1
       d.quantize(Decimal('0.01'))

    将与Decimal('10000000.00')产生结果

  • 使以上干燥:
  • 1
    2
    3
        def round_decimal(number, exponent='0.01'):
            decimal_value = Decimal(number)
            return decimal_value.quantize(Decimal(exponent))

    1
    2
    3
        def round_decimal(number, decimal_places=2):
            decimal_value = Decimal(number)
            return decimal_value.quantize(Decimal(10) ** -decimal_places)
  • 赞成这个答案:)
  • 附:批评他人:格式不是四舍五入。


    要将数字四舍五入为一个分辨率,最好的方法是以下方法,它可以与任何分辨率一起使用(两个小数点或其他步骤为0.01):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> import numpy as np
    >>> value = 13.949999999999999
    >>> resolution = 0.01
    >>> newValue = int(np.round(value/resolution))*resolution
    >>> print newValue
    13.95

    >>> resolution = 0.5
    >>> newValue = int(np.round(value/resolution))*resolution
    >>> print newValue
    14.0


    我使用的方法是字符串切片。它相对简单快捷。

    首先,将浮点转换为字符串,然后选择希望的长度。

    1
    float = str(float)[:5]

    在上面的单行中,我们将值转换为一个字符串,然后只将该字符串保留为前四位数字或字符(包括前四位数字或字符)。

    希望有帮助!