ChatGPT解决这个技术问题 Extra ChatGPT

将浮点数限制为两位小数

我希望将 a 四舍五入到 13.95。我尝试使用 round

>>> a
13.949999999999999
>>> round(a, 2)
13.949999999999999
嗯...您是要代表货币吗?如果是这样,你不应该用花车换美元。您可能可以将浮点数用于便士,或者您尝试建模的最小通用货币单位恰好是,但最佳做法是使用十进制表示,正如 HUAGHAGUAH 在他的回答中所建议的那样。
重要的是不要以浮动形式表示货币。浮点数不精确。但是便士或美分的金额是整数。因此整数是表示货币的正确方式。
@Basic,这取决于(大多数情况下没有)。以美分或便士为单位使用整数是傻瓜证明。它是代表金钱的行业标准。如果你知道自己在做什么,对浮点运算和 python 的十进制类有很好的理解,你可能会使用十进制。但这很大程度上取决于您的问题。你需要任意精度的小数吗?还是只有两位数?如果两位数:整数。它让你远离麻烦。我在一家银行软件咨询公司工作。
我可能来得太晚了,但是我想问一下,Python的开发人员解决了这个问题吗?因为当我做 round(13.949999999999999, 2) 时,我只得到 13.95。我已经在 Python 2.7.6 和 3.4 中尝试过。有用。不确定 2009 年是否有 2.7。也许它是 Python 2.5 的东西?
@bad_keypoints:是的,舍入问题已由 Python 2.7.0+ 解决。 my answer 中的更多内容

L
LeBorgne

您遇到了带有浮点数的 old problem,并非所有数字都可以精确表示。命令行只是向您显示内存中的完整浮点形式。

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

双精度数的精度为 53 位(16 位),而普通浮点数的精度为 24 位(8 位)。 floating point type in Python uses double precision 用于存储值。

例如,

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

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

使用整数并以美分而不是美元存储值,然后除以 100 以转换为美元。或者使用像十进制这样的定点数。


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

有新的格式规范,String Format Specification Mini-Language

您可以执行以下操作:

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

要添加逗号,您也可以 '{0:,.2f}'.format(1333.949999999) 打印 '1,333.95'
@OnurYıldırım:是的,但你可以用 float() 包装它; float("{0:.2f}".format(13.9499999))
@JossefHarush 你可以用 float() 包装它,但你什么也没得到。现在你又有了一个浮点数,同样不精确。 13.9499999999999 和 13.95 是相同的浮点数。
@NedBatchelder:我同意它们是相等的,但这将浮点数限制为两个小数点:)
顺便说一句,从 Python 3.6 开始,我们可以使用 f-strings:f"Result is {result:.2f}"
P
Peter Mortensen

内置 round() 在 Python 2.7 或更高版本中运行良好。

例子:

>>> round(14.22222223, 2)
14.22

查看the documentation


那么我是否理解这是 Python 2.7 的失败?为什么这样一个基本函数会从 v 2.7 到 v 3 产生不同的结果?
但是 round(2.16, 1) 给出 2.2 为什么 python 只提供 truncate 函数
例如,如果您尝试将值 2.675 舍入到小数点后两位,则会得到 >>> round(2.675, 2) 2.67 docs.python.org/2/tutorial/floatingpoint.html
来自 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.
请注意,如果您尝试使用此方法打印出诸如 1.00000 之类的数字,则无论您指定多少小数点,它都只会打印出 1.0。
M
Matt Fletcher

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

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

它也适用于更长的示例,使用运算符并且不需要括号:

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

A
Armali

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

例如:

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

13.95

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


佚名

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

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

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


P
Peter Mortensen

利用

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

代替

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

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


我的意思是, print"{0:.2f} {0:.2f}".format(a, b) 会导致输出错误 - 它会输出 'a' 值两次。而 print"{:.2f} {:.2f}".format(a, b) 将输出 'a' 和 'b' 值。
对于 Python 3,您只需要添加括号 print(...)。我写的都是对的。
如果您有两个变量,您将在 print("{0:.2f} {1:.2f}".format(a, b)) 之后
P
Peter Mortensen

试试下面的代码:

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

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

TLDR ;)

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)

输出字符串函数 str(float(...)) 在 Python < 中被四舍五入为 12 个有效数字。 2.7x 和 < 3.1,防止过多的无效数字类似于未固定的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.

文档

Release notes Python 2.7 - Other Language Changes第四段:

浮点数和字符串之间的转换现在在大多数平台上都可以正确舍入。这些转换发生在许多不同的地方:浮点数和复数上的 str(); float 和 complex 构造函数;数字格式;使用 marshal、pickle 和 json 模块对浮点数和复数进行序列化和反序列化;解析 Python 代码中的浮点数和虚数文字;和小数到浮点数的转换。与此相关,浮点数 x 的 repr() 现在返回基于最短十进制字符串的结果,该字符串保证在正确舍入(使用半舍入到偶数舍入模式)下舍入为 x。以前它给出了一个基于将 x 舍入为 17 位十进制数字的字符串。

The related issue

更多信息: 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 舍入确实不能比 Python 2.7 做得更好。大多数答案都是在 2.7 之前写的,但是它们已经过时了,尽管它们最初非常好。这就是我回答的原因。
包含“隐藏位”时为 53 位,隐式为 1,“逐渐下溢”期间除外。
不是圆的错,是显示器的错。
是的,这是众所周知的。但是,如果您反对 Python 2.7 发行说明或我的文本中的某些内容,或者根本不反对,我会错过一个上下文。它比这个问题的目的更复杂。应该补充的是,由于 rounding bug on certain 32-bit Intel chips 以及“round() 函数也 now 正确舍入”,Python 2.7 中也修复了从字符串到浮点数的转换。 (Release notes - 3.1 features backported to 2.7)。你能同意吗?
糟糕,这是 a*bb*a。感谢您的链接——怀旧。
I
Irfan wani
float_number = 12.234325335563
round(float_number, 2)

这将返回;

12.23

解释:

round 函数有两个参数;要四舍五入的数字和要返回的小数位数。这里我返回了 2 个小数位。


但是,如果我们说 0.093,您是如何获得额外的 0 的。这给了我 0.1 作为答案
如果您返回 2 个小数位或通常比左侧小数部分中存在的零的数量多 1 个位,那么您将获得所需的正确结果。例如,如果您仅将我的答案应用于数字 0.093,它会返回 0.09 但如果你只想得到 1 位小数,那么当然,它会返回 0.1 因为 0.0 是完全错误的。(我的代码以同样的方式工作。也许你只想得到 1 位小数。如果要想得到更准确的结果,就必须增加小数位数。)
根据文档,返回十进制是不正确的`如果省略ndigits或None,则返回值为整数。否则返回值与数字具有相同的类型。 ndigits 可能是负数。`
所有这些虚假的赞成票。您的答案只是重复了十年前提出问题时 OP 发布的代码。 OP 知道 round 函数。你根本没有解决他的问题。 (今天甚至不存在这个问题。)
这不适用于更大的数字。 Round 的行为不像人们想要的那样。我们想要十进制格式而不是圆形。 99.9999999987 不应该变成 100 它应该是 99.99。这就是人们想要解决的问题。简单的小数学是不费吹灰之力的。
P
Peter Mortensen

您可以修改输出格式:

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

P
Peter Mortensen

对于 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),选项二是 preferred

有关选项二的更多信息,我建议在 string formatting from the Python documentation 上使用此链接。

有关选项一的更多信息,this link will suffice and has information on the various flags

参考:Convert floating point number to a certain precision, and then copy to string


你如何表示一个整数?如果我使用 "{i3}".format(numvar) 我得到一个错误。
这就是我的意思:如果 numvar=12.456,则 "{:.2f}".format(numvar) 产生 12.46"{:2i}".format(numvar) 给出错误,我期待 12
A
Asad Manzoor

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

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

这将返回字符串
如果我们只想截断,如何获得。
S
Shashank Singh

在 Python 2.7 中:

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

这根本没有帮助。 outputa 具有 完全相同 值,因此您不妨在最后一行写 print a 而不是 print output
@MarkDickinson 请您再试一次。因为它在我的编译器中按预期运行。
你错过了我的观点。是的,您的代码打印 13.95。但是在 Python 2.7 中,对于 a 的这个特定值,print a 也是如此,所以还不清楚格式化步骤的重点是什么。
@MarkDickinson 我已经编辑了代码。我同意“打印 a”确实打印与“打印输出”相同的值。但是如果你比较“a==output”,结果将是“False”,因为格式化步骤确实将浮点值“a”四舍五入到小数点后两位。
您是否真的为您显示的代码尝试了 a == output?它为我提供 True,我怀疑它也为您提供。
t
toto_tico

正如@Matt 所指出的,Python 3.6 提供了 f-strings,他们也可以使用 nested parameters

value = 2.34558
precision = 2
width = 4

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

这将显示 result: 2.35


p
passionatedevops

我们有多种选择:选项1:

x = 1.090675765757
g = float("{:.2f}".format(x))
print(g)

选项 2:内置的 round() 支持 Python 2.7 或更高版本。

x = 1.090675765757
g =  round(x, 2)
print(g)

问题具体说round方法没有做他想做的事。查看 this answer 了解有关原因的更多信息
P
Peter Mortensen

Python 教程有一个名为 Floating Point Arithmetic: Issues and Limitations 的附录。阅读。它解释了正在发生的事情以及为什么 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 ... >>> 总和 0.99999999999999989

您的问题的一种替代方案和解决方案是使用 decimal 模块。


J
Jonathan L

使用 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')

不错的图标。 online-go.com/user/view/179
P
Peter Mortensen

它正在做你告诉它做的事情并且工作正常。阅读有关 floating point confusion 的更多信息,也许可以尝试使用 decimal 对象。


w
weaming
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'

我们可以返回 float 而不是 Decimal?喜欢:def round_float(v, ndigits=2) -> float: d = Decimal(v); v_str = ("{0:.%sf}" % ndigits).format(round(d, ndigits)); return float(v_str)
@alper 你可以返回任何你喜欢的东西
M
MikeL
orig_float = 232569 / 16000.0

14.5355625

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

14.54


P
Peter Mortensen

为了修复 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

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

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

getcontext().prec = 6 仅适用于函数范围或所有地方?
上下文是算术运算的环境。它们控制精度,设置舍入规则,确定哪些信号被视为例外,并限制指数范围。每个线程都有自己的当前上下文@JulioMarins
G
Gustavo Mirapalheta

像这样的 lambda 函数怎么样:

arred = lambda x,n : x*(10**n)//1/(10**n)

这样你就可以这样做:

arred(3.141591657,2)

并得到

3.14

S
Sławomir Lenart

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

使用十进制模块进行快速正确舍入的十进制浮点运算:d=Decimal(10000000.0000009)

实现四舍五入:

   d.quantize(Decimal('0.01'))

将产生 Decimal('10000000.00')

使上述干燥:

    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)

赞成这个答案:)

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


P
Praveen Kumar

简单的解决方案在这里

value = 5.34343
rounded_value = round(value, 2) # 5.34

这里有很多不必要的东西。 8/3 已经属于 float 类型,因此 float 调用没有任何用处。直接调用 dunder 方法有点奇怪 - 相反,只需调用委托给这些 dunder 方法的函数。所以拼写第一行的更好方法就是value = round(8/3, 2)。到那时,您并没有真正添加其他答案中没有的任何内容。
对不起,不相关的答案。我认为这是正确的方法。另外,在问题部分,他提到round方法不起作用,所以我没有检查它。
S
Shoaib Muhammad Arif

这是使用格式功能的简单解决方案。

float(format(num, '.2f'))

注意:我们将数字转换为浮点数,因为格式方法返回字符串。


Y
Yogesh Yadav

如果你想处理钱,使用 python decimal 模块

from decimal import Decimal, ROUND_HALF_UP

# amount can be integer, string, tuple, float, or another Decimal object
def to_money(amount) -> Decimal:
    money = Decimal(amount).quantize(Decimal('.00'), rounding=ROUND_HALF_UP)
    return money

我尝试将它与 DateFrame 的列一起使用。收到消息:TypeError:不支持从系列到小数的转换
R
Ray Tayek

lambda x,n:int(x*10^n+.5)/10^n 多年来为我工作了很多语言。


似乎连续两个“*”消失了。
P
Peter Mortensen

要将数字四舍五入为分辨率,最好的方法是以下一种,它可以用于任何分辨率(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 >>> value = 0.184 >>> np.round(value/res) * res 0.17999999999999999
查找文档我发现问题来自 numpy.round 准确性/精度。所以它需要在与分辨率相乘之前将其定义为 int 。我更新了代码。谢谢你!
唯一需要的是将 np.round 的 numpy.float64 结果转换为 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 的信息现在已添加到 my answer 中,您可能会误投反对票。最初与 numpy 无关。
@hynekcer 我不认为我的回答是最好的。只是想添加一个限制浮点数到 n 小数但最接近定义分辨率的示例。正如您所说,我检查过,int您也可以使用 float@szeitlin 示例来代替。感谢您的额外评论。 (对不起,我没有对你投反对票)
为数字处理(熊猫)添加全新的依赖项是“最佳方式”吗?
N
NMC

我看到的答案不适用于 float(52.15) 案例。经过一些测试,有我正在使用的解决方案:

import decimal
        
def value_to_decimal(value, decimal_places):
    decimal.getcontext().rounding = decimal.ROUND_HALF_UP  # define rounding method
    return decimal.Decimal(str(float(value))).quantize(decimal.Decimal('1e-{}'.format(decimal_places)))

('value' 到 float 再到 string 的转换非常重要,这样,'value' 的类型可以是 float、decimal、integer 或 string!)

希望这对任何人都有帮助。