将浮点数限制为两位小数


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.03.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()代表浮点数和复数;浮动和复杂的构造函数;数字格式;串行化,并使用反序列浮子和复数marshalpicklejson模块; 在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.