如何在Python中正确地舍入一半浮点数?

Del*_*gan 46 python floating-point precision rounding python-3.x

我正面临着一个奇怪的round()功能行为:

for i in range(1, 15, 2):
    n = i / 2
    print(n, "=>", round(n))
Run Code Online (Sandbox Code Playgroud)

此代码打印:

0.5 => 0
1.5 => 2
2.5 => 2
3.5 => 4
4.5 => 4
5.5 => 6
6.5 => 6
Run Code Online (Sandbox Code Playgroud)

我希望浮动值总是向上舍入,而是舍入到最接近的偶数.

为什么会出现这种行为,以及获得正确结果的最佳方法是什么?

我尝试使用fractions但结果是一样的.

Mar*_*ers 40

" 数字类型"部分明确记录了此行为:

round(x[, n])
x舍入为n位数,舍入为偶数的一半.如果省略n,则默认为0.

注意四舍五入到偶数.这也被称为银行家四舍五入 ; 而不是总是向上或向下舍入(复合舍入误差),通过舍入到最接近的偶数来平均舍入误差.

如果您需要更多地控制舍入行为,请使用该decimal模块,该模块允许您准确指定应使用的舍入策略.

例如,从一半向上舍入:

>>> from decimal import localcontext, Decimal, ROUND_HALF_UP
>>> with localcontext() as ctx:
...     ctx.rounding = ROUND_HALF_UP
...     for i in range(1, 15, 2):
...         n = Decimal(i) / 2
...         print(n, '=>', n.to_integral_value())
...
0.5 => 1
1.5 => 2
2.5 => 3
3.5 => 4
4.5 => 5
5.5 => 6
6.5 => 7
Run Code Online (Sandbox Code Playgroud)

  • https://en.wikipedia.org/wiki/Rounding#Round_half_to_even 还描述了 IEEE 754 四舍五入的一半到偶数 (2认同)

dho*_*bbs 25

例如:

from decimal import Decimal, ROUND_HALF_UP

Decimal(1.5).quantize(0, ROUND_HALF_UP)

# This also works for rounding to the integer part:
Decimal(1.5).to_integral_value(rounding=ROUND_HALF_UP)
Run Code Online (Sandbox Code Playgroud)


小智 15

你可以用这个:

import math
def normal_round(n):
    if n - math.floor(n) < 0.5:
        return math.floor(n)
    return math.ceil(n)
Run Code Online (Sandbox Code Playgroud)

它将正确地向上或向下舍入数字.

  • 我一次又一次地感到震惊,没有这样的内部功能。我的意思是,这不像现在的人们在 python 中使用大量的 numpy 和 math 来实现数值算法...... (2认同)

Tea*_*ast 15

为什么要把事情搞得这么复杂呢?(仅适用于正数

def HalfRoundUp(value):
    return int(value + 0.5)
Run Code Online (Sandbox Code Playgroud)

你当然可以将它变成一个 lambda ,它是:

HalfRoundUp = lambda value: int(value + 0.5)
Run Code Online (Sandbox Code Playgroud)

不幸的是,这个简单的答案不适用于负数,但可以使用数学中的下限函数来修复:(这也适用于正数和负数

from math import floor
def HalfRoundUp(value):
    return floor(value + 0.5)
Run Code Online (Sandbox Code Playgroud)

  • 不会为其他答案增加任何价值。 (7认同)
  • @simomo Python 的“round()”函数在旧版本中用于四舍五入,但在 Python 3 中他们将舍入策略改为一半甚至偶数。这是一个经过深思熟虑的选择,因为这会产生更好的结果。请参阅:https://docs.python.org/3/whatsnew/3.0.html#builtins (2认同)

Mat*_*eld 14

round()将向上或向下舍入,具体取决于数字是偶数还是奇数.只围绕一个简单的方法是:

int(num + 0.5)
Run Code Online (Sandbox Code Playgroud)

如果您希望此功能正确用于负数,请使用:

((num > 0) - (num < 0)) * int(abs(num) + 0.5)
Run Code Online (Sandbox Code Playgroud)

注意,对于大量或像真正精确的数字这可以搞砸5000000000000001.00.49999999999999994.

  • 这个解决方案没有解决一些细微之处.例如,如果`num = -2.4`,这会给出什么结果?怎么样`num = 0.49999999999999994`?`num = 5000000000000001.0`?在使用IEEE 754格式和语义的典型计算机上,此解决方案为所有这三种情况提供了错误的答案. (2认同)

小智 6

喜欢fedor2612 的答案。对于那些想要使用此函数来舍入任意数量的小数的人(例如,如果您想将货币 26.455 美元舍入为 26.46 美元),我使用可选的“小数”参数对其进行了扩展。

import math

def normal_round(n, decimals=0):
    expoN = n * 10 ** decimals
    if abs(expoN) - abs(math.floor(expoN)) < 0.5:
        return math.floor(expoN) / 10 ** decimals
    return math.ceil(expoN) / 10 ** decimals

oldRounding = round(26.455,2)
newRounding = normal_round(26.455,2)

print(oldRounding)
print(newRounding)
Run Code Online (Sandbox Code Playgroud)

输出:

26.45

26.46


Pat*_*han 5

您看到的行为是典型的IEEE 754舍入行为。如果必须在两个与输入相同的数字之间进行选择,则始终选择偶数。此行为的优点是平均舍入效果为零-上下舍入的数字相同。如果沿一致的方向四舍五入数字,则四舍五入将影响期望值。

如果目标是四舍五入,那么您看到的行为是正确的,但这并不总是需要的。

获得所需舍入类型的一种技巧是添加0.5,然后取整。例如,将0.5与2.5相加得到3,底数为3。