将浮点数限制在两位小数。

2348

12
嗯...你是在尝试表示货币吗?如果是的话,你不应该使用浮点数表示美元。你可以使用浮点数表示便士,或者你正在尝试建模的最小通用货币单位,但最佳实践是使用十进制表示法,正如HUAGHAGUAH在他的答案中建议的那样。 - SingleNegationElimination
89
不要将货币表示为浮点数,而应该使用整数来表示分或美分数额。浮点数不够精确,而整数能够准确地表示货币数值。因此,整数是正确表示货币的方法。 - Davoud Taghawi-Nejad
5
基本上不需要(大多数情况下),用以分为单位的整数或者以便士为单位是很可靠的。这是代表货币的行业标准。如果你知道你在做什么,对浮点算术和Python的十进制类有充分的了解,可能会使用十进制表示法。但这取决于你的问题。你需要任意精度的小数吗?还是只需要两位数字?如果是两位数字:整数足矣。它会避免让你陷入麻烦。出处:我曾在银行软件咨询公司工作过。 - Davoud Taghawi-Nejad
35
我可能来得有点晚了,但我想问一下,Python的开发者已经解决了这个问题吗?因为当我执行round(13.949999999999999, 2)时,结果就是13.95。我已经在Python 2.7.6和3.4中尝试了一下,都可以工作。不确定2009年是否已经有Python 2.7版本。也许这只是Python 2.5的问题? - bad_keypoints
10
是的,Python 2.7.0+ 已经解决了舍入问题。更多信息请参见我的回答 - hynekcer
显示剩余6条评论
37个回答

2252
你遇到了有关浮点数的 旧问题,并不是所有数字都能被准确地表示。命令行只是从内存中显示完整的浮点形式。
使用浮点数表示时,你的四舍五入版本是同样的数字。由于计算机是二进制的,它们将浮点数存储为整数,然后除以2的幂,所以13.95将类似于125650429603636838/(2 ** 53)这样表示。
双精度数具有53位(16位数字)的精度,常规浮点数具有24位(8位数字)的精度。Python 中的浮点类型使用双精度 来存储值。
例如:
>>> 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("{:.2f}".format(a))
13.95
>>> print("{:.2f}".format(round(a, 2)))
13.95
>>> print("{:.15f}".format(round(a, 2)))
13.949999999999999

如果您只需要显示两位小数(例如显示货币值),那么您有几个更好的选择:

  1. 使用整数并将值存储为分而不是美元,然后除以100转换为美元。
  2. 或者使用固定点数,如decimal

42
@Christian:存储的值和如何“显示”这个值之间存在根本性差异。格式化输出应该允许您根据需要添加填充,以及添加逗号分隔符等。 - Basic
31
值得一提的是,"%.2f" % round(a,2) 不仅可以放在 printf 中使用,也可以用在像 str() 这样的函数中。 - andilabs
31
为什么人们总是默认货币采用浮点舍入?有时候你只想使用较少的精度。 - worc
11
你需要理解二进制值(类型为 float)只是最接近的十进制数(你作为人类熟悉的数)的可用近似值。0.245 这样的(有限表示的)二进制值不存在,从数学上讲也不可能存在。最接近 0.245 的二进制值略微小于 0.245,因此自然会向下舍入。同样,0.225 在二进制中不存在,但是最接近 0.225 的二进制值略微大于 0.225,因此自然会向上舍入。 - John Y
17
@radtek:你确实要求解释。最直接的解决方案是使用Decimal,这也是这个答案中提出的解决方案之一。另一个解决方案是将您的量转换为整数并使用整数算术运算。这两种方法也出现在其他答案和评论中。 - John Y
显示剩余14条评论

795

现在有新的格式规范,即字符串格式规范迷你语言

您可以执行与以下内容相同的操作:

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

注意1:上述代码返回一个字符串。如果需要转换为浮点数,只需使用float(...)将其包装起来即可:

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

注意2:使用float()进行包装不会改变任何内容:

>>> x = 13.949999999999999999
>>> x
13.95
>>> g = float("{:.2f}".format(x))
>>> g
13.95
>>> x == g
True
>>> h = round(x, 2)
>>> h
13.95
>>> x == h
True

27
如果想要加逗号,可以使用'{0:,.2f}'.format(1333.949999999),输出结果为'1,333.95' - Stephen Blum
5
你可以使用float()将其包装起来,但这样并没有获得任何东西。现在你又得到了一个浮点数,带有相同的不精确性。13.9499999999999和13.95是相同的浮点数。 - Ned Batchelder
4
我同意它们是相等的,但这将浮点数限制为两个小数点。 - Jossef Harush Kadouri
没有办法“四舍五入”一个数字。格式化只是为了美观输出而已。如果你真的需要存储数字(例如跟踪金钱),请使用整数。 - Neil
55
顺便提一下,自从Python 3.6版本开始,我们可以使用f字符串:f"Result is {result:.2f}"。该字符串会将result的值插入到花括号中,并格式化为保留两位小数的浮点数。 - Andrey Semakin
显示剩余3条评论

427

在Python 2.7或更高版本中,内置的round()函数可以正常使用。

示例:

>>> round(14.22222223, 2)
14.22

查看文档


1
我是否应该理解这是Python 2.7的失败?为什么这样一个基础函数在2.7版和3版中会产生不同的结果呢? - Michael M
1
但是 round(2.16, 1) 返回 2.2,为什么 Python 只提供了一个 truncate 函数呢? - jiamo
2
例如,如果您尝试将值2.675舍入到两个小数位,您会得到以下结果: >>> round(2.675, 2) 2.67https://docs.python.org/2/tutorial/floatingpoint.html - Melroy van den Berg
10
Python 3 文档页面中的注释:注意,对于浮点数,round() 函数的行为可能会让人惊讶:例如,round(2.675, 2) 返回的结果是 2.67 而不是期望的 2.68。这不是一个 bug,而是因为大多数十进制小数无法精确表示为浮点数的结果。 - Richard Dally
3
请注意,如果您尝试使用此方法打印类似于1.00000的数字,它将仅打印出1.0,无论您指定多少小数点。 - Josh Correia
显示剩余3条评论

343
让我用Python 3.6的f-string/模板字符串格式给你举个例子,我觉得这个格式非常简洁美观。
>>> f'{a:.2f}'

它也可以很好地处理更长的示例,包括运算符,而不需要括号。
>>> print(f'Completed in {time.time() - start:.2f}s')

181

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

例如:

a = 13.949999999999999
format(a, '.2f')

13.95

这将生成一个浮点数字符串,精确到小数点后两位。


101

大多数数字在浮点数中无法精确表示。如果您想要四舍五入数字,因为这是您的数学公式或算法所需的,那么您需要使用 round 函数。如果您只想将显示限制为某个特定精度,则甚至不需要使用 round 函数,只需将其格式化为该字符串即可。(如果您想使用某些替代的舍入方法进行显示,而且有很多种方法,那么您需要混合这两种方法。)

>>> "%.2f" % 3.14159
'3.14'
>>> "%.2f" % 13.9499999
'13.95'

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

101

使用

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

取代

print"{0:.2f}".format(a)
因为后者在尝试输出多个变量时可能会导致输出错误(请参见注释)。

1
我的意思是,print"{0:.2f} {0:.2f}".format(a, b) 会导致输出错误 - 它会两次输出'a'的值。而 print"{:.2f} {:.2f}".format(a, b) 将输出'a'和'b'的值。 - Alexey Antonenko
2
对于 Python 3,您只需要添加括号 print(...),并且其中所有我编写的内容都是正确的。 - Alexey Antonenko
3
如果你有两个变量,想要输出"{0:.2f} {1:.2f}"格式的内容,你需要使用print("{0:.2f} {1:.2f}".format(a, b))。 - Hovo

73

简述 ;)

Python 3.1彻底解决了输入输出的舍入问题,并将修复程序反向移植到Python 2.7.0。可以在浮点数和字符串之间进行可逆转换:str -> float() -> repr() -> float() ... 或者 Decimal -> float -> str -> Decimal

>>> 0.3
0.3
>>> float(repr(0.3)) == 0.3
True

现在不再需要使用 Decimal 类型进行存储。

算术运算的结果必须重新进行舍入,因为舍入误差可能会积累更多的不准确性,超出解析一个数字可能产生的程度。这并没有通过改进的repr()算法来解决(Python >= 3.1, >=2.7.0):

>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1, 0.2, 0.3
(0.1, 0.2, 0.3)

在Python < 2.7x和 < 3.1版本中,输出字符串函数str(float(...))的有效数字被舍入为12个,以防止类似于未修复的repr()输出的非法数字过多。即使减去非常相似的数字后仍然不足,而在其他操作后则会过度舍入。Python 2.7和3.1使用相同长度的str(),尽管repr()已被修复。一些旧版本的Numpy甚至在使用修复的Python时也存在非法数字过多的情况。当前的Numpy已修复此问题。 Python版本>= 3.2的str()和repr()函数及Numpy中类似函数的输出结果相同。


测试

import random
from decimal import Decimal
for _ in range(1000000):
    x = random.random()
    assert x == float(repr(x)) == float(Decimal(repr(x)))  # Reversible repr()
    assert str(x) == repr(x)
    assert len(repr(round(x, 12))) <= 14         # no excessive decimal places.

文档

请查看Python 2.7发布说明的第四段中的其他语言更改

现在,在大多数平台上,浮点数和字符串之间的转换已经正确四舍五入。这些转换发生在许多不同的地方:在浮点数和复数上的str();在float和complex构造函数上;在数字格式化、使用marshal、pickle和json模块序列化和反序列化浮点数和复数;在Python代码中解析浮点数和虚数文字;以及Decimal-to-float转换。

与此相关的是,浮点数x的repr()现在返回一个基于最短十进制字符串的结果,该字符串保证在正确舍入(使用四舍六入五取偶舍入模式)下回到x。先前,它给出了基于将x舍入为17个小数位的字符串。

相关问题


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


为什么被踩了?这个问题是关于Python的float(双精度)和普通的round,而不是关于numpy.double及其转换为字符串。在Python 2.7中,普通的Python舍入确实无法做得比这更好了。大多数答案都是在2.7之前编写的,但它们已经过时了,尽管它们最初非常好。这就是我回答的原因。 - hynekcer
当包括“隐藏位”时,共有53位,该位在“逐渐下溢”期间隐含为1。 - Rick James
这不是 round 的错,而是显示器的问题。 - Rick James
是的,这个很出名。然而,如果您反对Python 2.7版本说明中的某些内容,或者反对我的文本,或者根本不反对任何内容,我都需要了解上下文。这个问题比必要的更复杂。值得一提的是,由于某些32位英特尔芯片的四舍五入错误,在Python 2.7中,字符串转换成浮点数也已经得到修复,而且“round()函数现在被正确地四舍五入了。”(版本说明-将3.1功能回溯到2.7)。你同意吗? - hynekcer
1
糟糕,那是 a*bb*a 的区别。感谢提供的链接 -- Nostalgia。 - Rick James
显示剩余2条评论

71

请尝试以下代码:

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

但请注意,a的值仍然是一个不精确的浮点数。在这里查看 - http://repl.it/LJs(在右侧顶部单击“运行会话”)。 - lifebalance
3
如果你选择这种方法,为了更准确地表示,应该添加0.5。int(a * 100 + 0.5) / 100.0; 使用math.ceil是另一种选项。 - arhuaco
3
@ShashankSawant说:“首先,这个答案没有四舍五入,而是截断。建议在结尾处加上一半将进行四舍五入,但与一开始使用“round”函数相比,没有任何好处。另外,由于这个解决方案仍然使用浮点数,因此即使对这个“解决方案”的“修正”版本进行了修正,OP的原始问题仍然存在。” - John Y
3
这只是对round函数的不必要重新实现(该函数已在问题中使用)。 - interjay
5
如果 round() 函数不起作用,就需要使用 @interjay 提到的东西。 - Pithikos
显示剩余3条评论

66

使用:

float_number = 12.234325335563
round(float_number, 2)

这将返回;

12.23

说明:

round函数接受两个参数:需要四舍五入的数字和保留的小数位数。这里保留了两位小数。


2
但是如果我们有0.093,你是怎么让额外的0显示出来的呢?这会给我答案0.1。 - Naman Jain
如果你返回2个小数位或者比小数部分左侧的零多1位,那么你将得到正确的结果。例如,如果你简单地将我的答案应用于数字0.093,它将返回0.09,但如果你只想要1个小数位,那么当然,它将返回0.1,因为0.0是完全错误的。(我的代码也是这样工作的。也许你只想要1个小数位。如果你想要更精确的结果,你必须增加小数位数。) - Irfan wani
根据文档,它并不会返回小数。如果省略或未设定 ndigits,则返回值为整数。否则,返回值的类型与输入数字相同,ndigits 可以为负数。 - Arar
所有这些虚假的点赞。你的答案只是重复了十年前OP发布问题时所发布的代码。OP已经知道了round函数。你根本没有用它解决他的问题。(这个问题甚至今天都不存在。) - aamarks
这对于更大的数字不起作用。Round函数的行为不符合人们的期望。我们需要一个十进制格式而不是四舍五入。99.9999999987不应该变成100,而应该是99.99。这是人们想要解决的问题。简单的小数计算是易如反掌的。 - Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接