将浮点数限制为两位小数


1689

我想a四舍五入到13.95。

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

该round功能无法按我预期的方式工作。



6
嗯...您要代表货币吗?如果是这样,则不应将浮点数用于美元。您可能会使用浮点数作为便士,或者尝试使用任何最小的通用货币单位,但最佳做法是使用小数表示法,如HUAGHAGUAH在其回答中建议的那样。
— SingleNegationElimination

63
重要的是不要以浮动货币表示。浮动并不精确。但是一分钱或几分钱是整数。因此,整数是表示货币的正确方法。
— 达沃德·塔哈维·内贾德

2
@ DavoudTaghawi-Nejad或更多…… 十进制类型
— 基本时间

17
我来这里可能太晚了,但是我想问一下,Python的开发人员是否解决了这个问题?因为当我进行舍入运算(13.949999999999999,2)时,我只得到13.95。我已经在Python 2.7.6和3.4中进行了尝试。有用。不知道2.7在2009年是否还存在。也许这是Python 2.5的事情?
— bad_keypoints 2015年

Answers:


1685

您正在碰到浮点数的旧问题,即并非所有数字都可以准确表示。命令行只是向您显示内存中的完整浮点形式。

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

双精度数字的精度为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. 或者使用定点数(如小数)。

27
@Christian存储的值和如何显示该值之间存在根本差异。格式化输出应允许您添加填充的要求,以及添加逗号分隔符,等等
— 基本

22
值得一提的是,"%.2f" % round(a,2)您不仅可以在printf中添加内容,而且还可以添加类似内容str()
— andilabs

20
人们为什么总是在浮点舍入中采用货币?有时您只想降低精度。
— worc 2014年

9
@radtek:您需要了解二进制值(类型float)只是十进制数字(您作为人类所熟悉的)的最接近的可用近似值。没有这样的(绝对可表示的)二进制值0.245。它根本不存在,并且在数学上不存在。最接近0.245的二进制值略小于 0.245,因此自然会四舍五入。同样,作为二进制0.225没有这样的事情,但二进制值最接近0.225稍微大于 0.225,因此它自然四舍五入。
— 约翰Y

12
@radtek:您确实是在要求解释。实际上,最直接的解决方案是使用Decimal,这是此答案中提出的解决方案之一。另一个是将您的数量转换为整数并使用整数算术。这两种方法也都出现在其他答案和评论中。
— 约翰Y

586

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

您可以执行以下操作:

"{:.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

17
要添加逗号也可以'{0:,.2f}'.format(1333.949999999)打印'1,333.95'。
— Stephen Blum 2014年

@OnurYıldırım:是的,但是您可以将其包裹起来float();float("{0:.2f}".format(13.9499999))
— 约瑟夫·哈鲁什

5
@JossefHarush可以用float()包装它,但是您什么都没得到。现在,您又有一个浮点数,并且具有相同的不精确度。13.9499999999999和13.95是相同的浮点数。
— Ned Batchelder 2014年

4
@NedBatchelder:我同意它们是相等的,但这将浮点数限制为两位小数点:)
— Jossef Harush

8
顺便说一下,由于Python 3.6,我们可以使用f字符串:f"Result is {result:.2f}"
— Andrey Semakin

289

内建round()在Python 2.7或更高版本中工作正常。

例:

>>> round(14.22222223, 2)
14.22

查看文档。


1
所以我能理解这是Python 2.7失败吗?为什么这样的基本函数从v 2.7到v 3会产生不同的结果?
— MikeM'9

但round(2.16, 1)给予2.2为什么蟒蛇只是提供了一个truncateFUNC
— 谟

例如,如果您尝试将值2.675舍入到小数点后两位,您将获得以下>>> round(2.675, 2) 2.67 docs.python.org/2/tutorial/floatingpoint.html
— 危险

4
从Python 3文档页面:Note The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float.
— Richard Dally,

请注意,如果您尝试使用此方法打印出一个数字(例如1.00000),则无论您指定多少小数点,它只会打印出1.0。
— 乔什·科雷亚

142

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

例如:

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

13.95

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


97

采用

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

代替

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

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


2
这是无稽之谈。给定的两个语句在Python 2.7上的行为相同,并且仅第二条语句在Python 2.6上有效。(这两个语句在Python 3或Python <2.6中均无效。)第一种形式除了简洁以外没有任何优势。
— 马克·迪金森

1
我的意思是,打印“ {0:.2f} {0:.2f}”。format(a,b)会导致输出错误-它将输出两次'a'值。在打印“ {:.2f} {:.2f}”。format(a,b)时将输出'a'和'b'值。
— 阿列克谢·安东

2
对于Python 3,您只需要添加方括号print(...)。在他们里面,我写的都是对的。
— 阿列克谢·安东

“我的意思是,打印” {0:.2f} {0:.2f}”。format(a,b)将导致输出“中的错误。啊。好吧,那是完全不同的说法!也许您应该编辑答案?(例如,“引发错误”在当前答案中意味着什么?您能举一个例子,说明第二个语句引发异常但第一个语句没有例外吗?)
— Mark Dickinson

3
如果您有两个变量,您将在print(“ {0:.2f} {1:.2f}”。format(a,b))之后
— Hovo

95

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

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

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


68

请尝试以下代码:

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

但请注意,a的值仍然是不精确的浮动。在这里查看-repl.it/LJ(单击“右侧”部分顶部的“运行会话”)。
— 2013年

3
如果采用这种方法,则应添加0.5以获得更准确的表示。int(a * 100 + 0.5)/ 100.0; 使用math.ceil是另一种选择。
— arhuaco

3
@ShashankSawant:嗯,一方面,给出的答案没有舍入,而是被截断了。建议在末尾增加一半,但是这样做比仅仅使用round函数没有好处。另一方面,因为此解决方案仍使用浮点,所以即使对于此“解决方案”的“更正”版本,OP的原始问题仍然存在。
— 约翰Y

3
-1,这只是功能的不必要的重新实现round(在问题中使用过)。
— interjay 2014年

4
@interjay,如果round()不能像前面提到的OP那样工作,则是必需的。
— Pithikos

57

TLDR;)

输入/输出的舍入问题已由Python 2.7.0和3.1 彻底解决。

正确舍入的数字可以可逆地来回转换:
str -> float() -> repr() -> float() ...或Decimal -> float -> str -> Decimal
不再需要使用十进制类型存储。


(自然地,可能有必要对舍入后的数字进行加或减运算,以消除累积的最后一位误码。显式的十进制算术仍然很方便,但是转换为字符串的方式是str()(即舍入到12个有效数字)通常足够好,如果不需要极高的精度或不需要极大量的连续算术运算。)

无限测试:

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-其他语言更改的第四段:

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

与此相关的是,浮点数x 的repr()现在基于最短的十进制字符串返回一个结果,该字符串保证在正确的舍入(使用“从一半到一半到四舍五入的舍入模式”下)可以四舍五入为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))使用numpy无法进行转换。另一方面:float.__repr__格式化,以便每个数字都很重要;顺序没有间隙,转换是可逆的。简单:如果您有一个numpy.float64数字,请将其转换为普通float,以便为人类(而非数字处理器)格式化,否则Python 2.7+不再需要。


为什么投票?问题是关于Python float(双精度)和normal的round,而不是numpy.double及其转换为字符串的问题。普通的Python舍入确实不能比Python 2.7更好。答案最多的是在2.7之前编写的,但是尽管它们本来就很好,但已过时了。这就是我回答的原因。
— hynekcer

包括“隐藏位”时为53位,这是隐式的1,“逐步下溢”期间除外。
— 瑞克·詹姆斯

这不是回合的错,是显示的错。
— 里克·詹姆斯

是的,这是众所周知的。但是,如果您反对Python 2.7发行说明中的​​内容或我的文本中的内容,或者什么都不反对,则我会错过上下文。这个问题的目的比必要的要复杂得多。应该添加的是,由于某些32位Intel芯片上的舍入错误,Python 2.7中还修复了从字符串到浮点的转换,并且“ round()函数现在也已正确舍入”。(发行说明-3.1功能已反向移植到2.7)。你可以同意吗?
— hynekcer

1
哎呀,这是a*bVS b*a。感谢您的链接-怀旧。
— 里克·詹姆斯

52

使用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文档中有关字符串格式的链接。

有关选项一的更多信息,此链接就足够了,并且具有有关各种标志的信息。

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


您如何表示整数?如果我使用“ {i3}”。format(numvar),则会收到错误消息。
— skytux 2013年

这就是我的意思:如果numvar=12.456,则"{:.2f}".format(numvar)产生12.46但"{:2i}".format(numvar)给出一个错误,并且我期望12。
— skytux 2013年


47

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

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

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

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


29

在Python 2.7中:

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

1
这根本没有帮助。output具有与完全相同的值a,因此您最好也写print a而不是print output在最后一行。
— 马克·迪金森

@MarkDickinson您能否再试一次。因为它在我的编译器中按预期运行。
— Shashank Singh '18

1
你想念我的意思。是的,您的代码会打印出来13.95。但是print a,对于apython 2.7中的特定值,也是如此,因此尚不清楚格式化步骤的重点。
— 马克·迪金森

@MarkDickinson我已经编辑了代码。我同意'print a'确实打印与“ print output”相同的值。但是,如果比较“ a == output”,结果将为“ False”,因为格式化步骤会将浮点值“ a”四舍五入到两位小数点。
— Shashank Singh,

1
您是否真的尝试a == output过显示的代码?它给True了我,我怀疑它也给你。
— 马克·迪金森

22

Python教程有一个附录,称为“ 浮点算术:问题和局限性”。阅读。它解释了正在发生的事情以及Python尽其所能的原因。它甚至有一个与您匹配的示例。让我引用一下:

>>> 0.1
0.10000000000000001

您可能会想使用该round() 函数将其切回到您期望的个位数。但这没有什么区别:

>>> round(0.1, 1)
0.10000000000000001

问题在于,存储的的二进制浮点值“0.1” 已经是与的最佳可能的二进制近似值。1/10,因此尝试再次对其进行舍入并不能使它更好:它已经足够好了。

另一个结果是,由于0.1 不完全精确1/10,将的十个值相加0.1可能不会精确地产生 1.0,或者:

>>> sum = 0.0
>>> for i in range(10):
...     sum += 0.1
...
>>> sum
0.99999999999999989

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


18

正如@Matt所指出的,Python 3.6提供了f-strings,它们也可以使用嵌套参数:

value = 2.34558
precision = 2
width = 4

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

将显示 result: 2.35



11

结合使用Decimal对象和round()方法。

Python 3.7.3
>>> from decimal import Decimal
>>> d1 = Decimal (13.949999999999999) # define a Decimal
>>> d1 
Decimal('13.949999999999999289457264239899814128875732421875')
>>> d2 = round(d1, 2) # round to 2 decimals
>>> d2
Decimal('13.95')

7

为了固定类型动态语言(例如Python和JavaScript)中的浮点,我使用了这种技术

# 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

您还可以按以下方式使用Decimal:

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
getcontext().prec = 6仅适用于功能范围或所有地方?
— 朱利奥·马林斯

1
上下文是算术运算的环境。它们控制精度,设置舍入规则,确定哪些信号被视为异常,并限制指数范围。每个线程都有其自己的当前上下文@JulioMarins
— Siamand '17

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

结果:

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'

6
orig_float = 232569 / 16000.0

14.5355625

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

14.54



4

就像1,2,3一样简单:

  1. 用十进制模块进行快速正确舍入的十进制浮点运算:

    d =十进制(10000000.0000009)

实现四舍五入:

   d.quantize(Decimal('0.01'))

将与 Decimal('10000000.00')

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

要么

    def round_decimal(number, decimal_places=2):
        decimal_value = Decimal(number)
        return decimal_value.quantize(Decimal(10) ** -decimal_places)
  1. 支持这个答案:)

PS:对他人的批评:格式不是四舍五入。


2

要将数字四舍五入为一种分辨率,最好的方法是使用以下方法,该方法可以在任何分辨率下工作(0.01表示两位小数,甚至其他步长):

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

在python 3.4.3和numpy 1.9.1上对我不起作用?>>>导入numpy为np >>> res = 0.01 >>>值= 0.184 >>> np.round(value / res)* res 0.17999999999999999
— szeitlin

1
寻找文档,我发现问题来自numpy.round准确性/准确性。因此,在与分辨率相乘之前,需要将其定义为int。我更新了代码。谢谢你!
— iblasi

唯一必要的是将numpy.float64np.round的结果转换为float或简单地使用round(value, 2)。在13.949999999999999(= 1395 / 100.)和3.950000000000001(= 1395 * .01)之间没有有效的IEEE 754号。您为什么认为自己的方法最好?原始值13.949999999999999289(= value = round(value,2))甚至比13.95000000000000178(np.float96打印)更精确。现在,我也将有关numpy的更多信息添加到我的答案中,您可能错误地投票了。本来不是numpy的。
— hynekcer

@hynekcer我认为我的答案不是最好的。只是想添加一个限制浮点数为n个小数但最接近已定义分辨率的示例。如您所说,我检查了一下,而不是int也可以使用float@szeitlin示例。感谢您的额外评论。(对不起,但我没有对您
— 投反对票

为数值处理(熊猫)添加全新的依赖性是“最佳方法”吗?
— Hejazzman

1

lambda x,n:int(x * 10 n + .5)/ 10 n已经为我使用多种语言提供了多年的服务。


-13

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

首先,将float转换为字符串,然后选择所需的长度。

float = str(float)[:5]

在上面的单行中,我们已将值转换为字符串,然后仅将字符串保留为其前四个数字或字符(包括首尾四个数字)。

希望有帮助!


2
请不要针对多个问题发布相同的答案。
— vaultah 2015年

18
哇... tdh ...切勿制作任何会计软件...如果数字恰好是113.94会发生什么?这将导致113.9 ...遗漏0.04。...这也已经有5年前的答案了。–
— Angry 84
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.