题 将浮点数限制为两个小数点


我想要 a 四舍五入到 13.95

>>> a
13.949999999999999
>>> round(a, 2)
13.949999999999999

round 功能不按我预期的方式工作。


1126
2018-01-18 18:16


起源


stackoverflow.com/questions/249467/...
嗯......你想代表货币吗?如果是这样,你不应该使用花车美元。您可以使用花车作为便士,或者您正在尝试建模的最小常见货币单位,但最佳做法是使用十进制表示,正如HUAGHAGUAH在他的回答中所建议的那样。 - SingleNegationElimination
重要的是不要在浮动中表示货币。浮子不准确。但便士或分数是整数。因此,整数是表示货币的正确方式。 - Davoud Taghawi-Nejad
我可能来得太晚了,但我想问一下,让Python的开发人员解决了这个问题吗?因为当我做圆(13.949999999999999,2)时,我只得到13.95。我在Python 2.7.6以及3.4中尝试过它。有用。不确定在2009年是否还有2.7。也许它是Python 2.5的东西? - bad_keypoints
@bad_keypoints:是的,Python 2.7.0+解决了舍入问题。更多 我的答案 这里 - hynekcer


答案:


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

在浮点数中,您的圆形版本是相同的数字。由于计算机是二进制的,它们将浮点数存储为整数,然后将其除以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("{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)或者使用固定点数 十进制


1161
2018-01-18 18:23



@Christian存储的值与您的存在方式存在根本区别 显示 那个价值。格式化输出应该允许您根据需要添加填充,以及添加逗号分隔符等。 - Basic
值得一提的是 "%.2f" % round(a,2) 你不仅可以放入printf,还可以放入类似的东西 str() - andilabs
为什么人们总是在浮点舍入上假设货币?有时你只是想以较低的精度工作。 - worc
@radtek:你需要了解二进制值(类型 float)只是十进制数的最接近可用近似值(您熟悉的是人类)。没有这样的(有限可表示的)二进制值为0.245。它根本就不存在,而且在数学上也是如此 不能 存在。最接近0.245的二进制值略有 少于 0.245,所以自然它向下舍入。同样地,二进制中没有0.225这样的东西,但是最接近0.225的二进制值是轻微的 比...更棒 0.225,所以很自然地它会变圆。 - John Y
@radtek:你确实要求解释。确实使用最直接的解决方案 Decimal,这是这个答案中提出的解决方案之一。另一种是将您的数量转换为整数并使用整数运算。这两种方法也出现在其他答案和评论中。 - John Y


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

您可以这样做:

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

注意 以上返回一个字符串。为了得到浮动,只需用 float(...)

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

注意 包裹着 float() 不改变任何东西:

>>> 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

423
2018-06-30 18:53



那会给你一个字符串。不是一个数字。 - Onur Yıldırım
你也可以添加逗号 '{0:,.2f}'.format(1333.949999999) 打印 '1,333.95'。 - Stephen Blum
@OnurYıldırım:是的,但你可以用它包装 float(); float("{0:.2f}".format(13.9499999)) - Jossef Harush
@JossefHarush你可以用float()包装它,但你还没有获得任何东西。现在你又有了一个浮动,同样的不精确。 13.9499999999999和13.95是相同的浮点数。 - Ned Batchelder
@NedBatchelder:我同意它们是相同的,但是这会将浮点数限制为两个小数点:) - Jossef Harush


内置的 round() 在Python 2.7或更高版本中工作得很好。

例:

>>> round(14.22222223, 2)
14.22

查看 文件


143
2017-12-31 10:51



我是否理解这是Python 2.7失败?为什么这样一个基本函数会产生与v 2.7到v 3不同的结果? - Michael Mügge
但 round(2.16, 1) 给 2.2 为什么python只提供一个 truncate FUNC - jiamo
这也适用于Python 2.7。在我看来,这个问题的最佳答案。 - alwbtc


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

例如:

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

13.95

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


118
2018-01-25 22:26



请注意,返回类型将是一个字符串。这不是我想要的。 - Dap
@Dap float(format(a, '.2f')) - T. Webster


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

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

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


85
2018-01-18 18:40





请尝试以下代码:

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

65
2017-08-26 06:46



但要注意,a的价值仍然是一个不精确的浮动。看看这里 - repl.it/LJs (单击右侧部分顶部的“运行会话”)。 - lifebalance
如果采用这种方法,则应添加0.5以获得更准确的表示。 int(a * 100 + 0.5)/ 100.0;使用math.ceil是另一种选择。 - arhuaco
@ShashankSawant:嗯,首先,所呈现的答案并不圆,它会截断。最后添加一半的建议将会四舍五入,但是仅仅使用这个就没有任何好处 round 功能首先。另一方面,因为这个解决方案仍然使用浮点,OP的原始问题仍然存在,即使对于这个“解决方案”的“更正”版本也是如此。 - John Y
-1,这只是一个不必要的重新实现 round 功能(在问题中使用)。 - interjay
@interjay这是必要的,如果 round() 不像OP提到的那样工作。 - Pithikos


使用Python <3(例如2.6或2.7),有两种方法可以做到这一点。

# 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是 首选

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

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

参考: 将浮点数转换为特定精度,然后复制到字符串


49
2017-12-11 06:37



你如何表示整数?如果我使用“{i3}”。format(numvar)我收到错误。 - skytux
这就是我的意思:如果 numvar=12.456, 然后 "{:.2f}".format(numvar) 产量 12.46 但 "{:2i}".format(numvar) 给出了一个错误,我期待着 12。 - skytux


您可以修改输出格式:

>>> a = 13.95
>>> a
13.949999999999999
>>> print "%.2f" % a
13.95

39
2018-01-18 18:31





使用

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

代替

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

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


37
2017-08-04 08:40



这是无稽之谈。给出的两个语句在Python 2.7上表现相同,只有第二个语句在Python 2.6上有效。 (这两种语句都不适用于Python 3或Python <2.6。)除了简洁之外,第一种形式没有任何优势。 - Mark Dickinson
我的意思是,打印“{0:.2f} {0:.2f}”。格式(a,b)将导致输出错误 - 它将输出'a'值两次。打印“{:。2f} {:。2f}”。format(a,b)将输出'a'和'b'值。 - Alexey Antonenko
对于Python 3,您只需要添加括号print(...)。我所写的一切都是对的。 - Alexey Antonenko
“我的意思是,打印”{0:.2f} {0:.2f}“。格式(a,b)将导致输出错误”。啊。嗯,这是一个完全不同的声明!也许你应该编辑你的答案? (例如,在当前答案中,“引发错误”的含义是什么?你能给出一个例子,说明第二个语句引发异常,但第一个没有?) - Mark Dickinson
如果你有两个变量,你将在打印后(“{0:.2f} {1:.2f}”。format(a,b)) - Hovo


TLDR;)

输入/输出的舍入问题一直存在 由Python 2.7.0解决 和 3.1 明确。

无限测试:

import random
for x in iter(random.random, None):           # Verify FOREVER that rounding is fixed :-)
    assert float(repr(x)) == x                # Reversible repr() conversion.
    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 the supermaket with Python 2.7+ :-)

文档

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

转换 现在,浮点数和字符串之间 正确舍入 在大多数平台上。这些转换发生在许多不同的地方:浮点数上的str()和复数;浮动和复杂的构造函数;数字格式;使用。序列化和反序列化浮点数和复数 marshalpickle 和 json 模块;在Python代码中解析float和imaginary文字;和十进制到浮点转换。

与此相关, 再版() 浮点数x现在返回基于的结果 最短的十进制字符串,保证回到x 在正确的舍入下(使用舍入到半舍入到舍入模式)。以前它给出了一个基于舍入x到17十进制数字的字符串。

相关问题


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


27
2018-01-31 18:33



为什么投票?问题是关于Python float (双精度)和正常 round,而不是关于numpy.double及其转换为字符串。简单的Python舍入确实不能比Python 2.7更好。大多数答案都是在2.7之前编写的,但是它们已经过时了,尽管它们原本非常好。这就是我回答的原因。 - hynekcer
包含隐藏位的“隐藏位”时为53位 1,除了“逐渐下溢”期间。 - Rick James
这不是圆形的故障,而是显示故障。 - Rick James
是的,众所周知。如果您在Python 2.7发行说明或我的文本中反对某些东西,或者根本没有任何内容,我会想念一个上下文。它比这个问题的目的更复杂。应该补充一点,因为在Python 2.7中已经修复了从字符串到float的转换 某些32位英特尔芯片上的错误 并且“圆()函数也是 现在 正确舍入。“(发行说明 - 3.1功能向后移植到2.7)。你同意吗? - hynekcer
糟糕,那是 a*b VS b*a。感谢您的链接 - Nostalgia。 - Rick James