216

我只是在重新阅读Python 3.0 中的新增功能,它指出:

round() 函数舍入策略和返回类型已更改。精确的中途情况现在四舍五入到最接近的偶数结果,而不是远离零。(例如,round(2.5) 现在返回 2 而不是 3。)

round的文档:

对于支持 round() 的内置类型,值被四舍五入到最接近的 10 的负 n 次方倍数;如果两个倍数同样接近,则朝偶数选择舍入

所以,在v2.7.3下:

In [85]: round(2.5)
Out[85]: 3.0

In [86]: round(3.5)
Out[86]: 4.0

正如我所预料的那样。但是,现在在v3.2.3下:

In [32]: round(2.5)
Out[32]: 2

In [33]: round(3.5)
Out[33]: 4

这似乎违反直觉,与我对四舍五入的理解相反(并且必然会绊倒人们)。英语不是我的母语,但在我读到这篇文章之前,我以为我知道四舍五入是什么意思:-/我确信在引入 v3 时一定有对此进行过一些讨论,但我无法找到一个很好的理由我的搜索。

  1. 有谁知道为什么会变成这个?
  2. 是否有任何其他主流编程语言(例如,C、C++、Java、Perl、 ..)可以进行这种(对我而言不一致的)舍入?

我在这里想念什么?

更新:@Li-aungYip 对“银行家的四舍五入”的评论给了我正确的搜索词/关键字来搜索,我发现了这个问题:为什么 .NET 使用银行家的四舍五入作为默认值?,所以我会仔细阅读。

4

11 回答 11

195

Python 3 的方式(称为“四舍五入”或“银行家的四舍五入”)如今被认为是标准的四舍五入方法,尽管一些语言实现还没有出现。

简单的“总是向上取整 0.5 ”技术会导致稍微偏向较大的数字。通过大量计算,这可能很重要。Python 3.0 方法消除了这个问题。

常用的舍入方法不止一种。IEEE 754 是浮点数学的国际标准,定义了五种不同的舍入方法(Python 3.0 使用的一种是默认方法)。还有其他人

这种行为并不像应有的那样广为人知。如果我没记错的话,AppleScript 是这种舍入方法的早期采用者。roundAppleScript 中的命令提供了几个选项,但与 IEEE 754 中一样,舍入到偶数是默认设置。显然,实现该round命令的工程师已经厌倦了“让它像我在学校学到的那样工作”的所有请求他实现了这一点:round 2.5 rounding as taught in school是一个有效的 AppleScript 命令。:-)

于 2012-05-31T00:24:15.787 回答
46

您可以使用Decimal 模块控制在 Py3000 中获得的舍入:

>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'), 
    rounding=decimal.ROUND_HALF_UP)
>>> Decimal('4')

>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'),    
    rounding=decimal.ROUND_HALF_EVEN)
>>> Decimal('2')

>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'), 
    rounding=decimal.ROUND_HALF_DOWN)
>>> Decimal('3')
于 2012-05-31T01:59:12.920 回答
20

只是在此处添加文档中的重要说明:

https://docs.python.org/dev/library/functions.html#round

笔记

浮点数的 round() 行为可能令人惊讶:例如,round(2.675, 2) 给出 2.67 而不是预期的 2.68。这不是错误:这是因为大多数小数部分不能完全表示为浮点数。有关详细信息,请参阅浮点算术:问题和限制。

因此,在 Python 3.2 中得到以下结果不要感到惊讶:

>>> round(0.25,1), round(0.35,1), round(0.45,1), round(0.55,1)
(0.2, 0.3, 0.5, 0.6)

>>> round(0.025,2), round(0.035,2), round(0.045,2), round(0.055,2)
(0.03, 0.04, 0.04, 0.06)
于 2014-07-02T14:54:47.203 回答
10

Python 3.x 将 0.5 值舍入为偶数的邻居

assert round(0.5) == 0
assert round(1.5) == 2
assert round(2.5) == 2

import decimal

assert decimal.Decimal('0.5').to_integral_value() == 0
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 2

但是,如果需要,可以将小数舍入“向后”更改为始终向上舍入 0.5:

decimal.getcontext().rounding = decimal.ROUND_HALF_UP

assert decimal.Decimal('0.5').to_integral_value() == 1
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 3

i = int(decimal.Decimal('2.5').to_integral_value()) # to get an int
assert i == 3
assert type(i) is int
于 2018-10-02T11:29:25.617 回答
8

我最近也遇到了这个问题。因此,我开发了一个 python 3 模块,它有 2 个函数 trueround() 和 trueround_precision() 来解决这个问题并给出与小学相同的舍入行为(不是银行家的舍入)。这是模块。只需保存代码并将其复制或导入即可。注意:trueround_precision 模块可以根据需要根据小数模块中的 ROUND_CEILING、ROUND_DOWN、ROUND_FLOOR、ROUND_HALF_DOWN、ROUND_HALF_EVEN、ROUND_HALF_UP、ROUND_UP 和 ROUND_05UP 标志更改舍入行为(有关更多信息,请参阅该模块文档)。对于下面的函数,请参阅文档字符串或使用 help(trueround) 和 help(trueround_precision) 如果复制到解释器中以获取更多文档。

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

def trueround(number, places=0):
    '''
    trueround(number, places)

    example:

        >>> trueround(2.55, 1) == 2.6
        True

    uses standard functions with no import to give "normal" behavior to 
    rounding so that trueround(2.5) == 3, trueround(3.5) == 4, 
    trueround(4.5) == 5, etc. Use with caution, however. This still has 
    the same problem with floating point math. The return object will 
    be type int if places=0 or a float if places=>1.

    number is the floating point number needed rounding

    places is the number of decimal places to round to with '0' as the
        default which will actually return our interger. Otherwise, a
        floating point will be returned to the given decimal place.

    Note:   Use trueround_precision() if true precision with
            floats is needed

    GPL 2.0
    copywrite by Narnie Harshoe <signupnarnie@gmail.com>
    '''
    place = 10**(places)
    rounded = (int(number*place + 0.5if number>=0 else -0.5))/place
    if rounded == int(rounded):
        rounded = int(rounded)
    return rounded

def trueround_precision(number, places=0, rounding=None):
    '''
    trueround_precision(number, places, rounding=ROUND_HALF_UP)

    Uses true precision for floating numbers using the 'decimal' module in
    python and assumes the module has already been imported before calling
    this function. The return object is of type Decimal.

    All rounding options are available from the decimal module including 
    ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, 
    ROUND_HALF_UP, ROUND_UP, and ROUND_05UP.

    examples:

        >>> trueround(2.5, 0) == Decimal('3')
        True
        >>> trueround(2.5, 0, ROUND_DOWN) == Decimal('2')
        True

    number is a floating point number or a string type containing a number on 
        on which to be acted.

    places is the number of decimal places to round to with '0' as the default.

    Note:   if type float is passed as the first argument to the function, it
            will first be converted to a str type for correct rounding.

    GPL 2.0
    copywrite by Narnie Harshoe <signupnarnie@gmail.com>
    '''
    from decimal import Decimal as dec
    from decimal import ROUND_HALF_UP
    from decimal import ROUND_CEILING
    from decimal import ROUND_DOWN
    from decimal import ROUND_FLOOR
    from decimal import ROUND_HALF_DOWN
    from decimal import ROUND_HALF_EVEN
    from decimal import ROUND_UP
    from decimal import ROUND_05UP

    if type(number) == type(float()):
        number = str(number)
    if rounding == None:
        rounding = ROUND_HALF_UP
    place = '1.'
    for i in range(places):
        place = ''.join([place, '0'])
    return dec(number).quantize(dec(place), rounding=rounding)

希望这可以帮助,

纳尼

于 2012-12-20T00:31:32.473 回答
4

python 3 中的 Python 2 舍入行为。

在小数点后 15 位加 1。精度高达 15 位。

round2=lambda x,y=None: round(x+1e-15,y)
于 2017-07-03T14:55:40.747 回答
2

一些案例:

in: Decimal(75.29 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(75.29 / 2, 2)
out: 37.65 GOOD

in: Decimal(85.55 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(85.55 / 2, 2)
out: 42.77 BAD

对于修复:

in: round(75.29 / 2 + 0.00001, 2)
out: 37.65 GOOD
in: round(85.55 / 2 + 0.00001, 2)
out: 42.78 GOOD

如果您想要更多小数,例如 4,您应该添加 (+ 0.0000001)。

为我工作。

于 2018-11-07T08:30:49.100 回答
0

样品复制:

['{} => {}'.format(x+0.5, round(x+0.5)) for x in range(10)]

['0.5 => 0', '1.5 => 2', '2.5 => 2', '3.5 => 4', '4.5 => 4', '5.5 => 6', '6.5 => 6', '7.5 => 8', '8.5 => 8', '9.5 => 10']

API:https ://docs.python.org/3/library/functions.html#round

状态:

返回小数点后四舍五入到 ndigits 精度的数字。如果 ndigits 被省略或为 None,则返回最接近其输入的整数。

对于支持 round() 的内置类型,值被四舍五入到最接近 10 的幂减去 ndigits 的倍数;如果两个倍数相等,则向偶数选择进行舍入(例如,round(0.5) 和 round(-0.5) 均为 0,round(1.5) 为 2)。任何整数值都对 ndigits 有效(正数、零或负数)。如果省略 ndigits 或 None,则返回值为整数。否则返回值的类型与数字相同。

对于一般的 Python 对象编号,将委托四舍五入到编号。圆形

注意 浮点数的 round() 行为可能令人惊讶:例如,round(2.675, 2) 给出 2.67 而不是预期的 2.68。这不是错误:这是因为大多数小数部分不能完全表示为浮点数。有关详细信息,请参阅浮点算术:问题和限制。

鉴于这种见解,您可以使用一些数学来解决它

import math
def my_round(i):
  f = math.floor(i)
  return f if i - f < 0.5 else f+1

现在您可以使用 my_round 而不是 round 来运行相同的测试。

['{} => {}'.format(x + 0.5, my_round(x+0.5)) for x in range(10)]
['0.5 => 1', '1.5 => 2', '2.5 => 3', '3.5 => 4', '4.5 => 5', '5.5 => 6', '6.5 => 7', '7.5 => 8', '8.5 => 9', '9.5 => 10']
于 2019-01-14T22:34:55.573 回答
-3

The easiest way to round in Python 3.x as taught in school is using an auxiliary variable:

n = 0.1 
round(2.5 + n)

And these will be the results of the series 2.0 to 3.0 (in 0.1 steps):

>>> round(2 + n)
>>> 2

>>> round(2.1 + n)
>>> 2

>>> round(2.2 + n)
>>> 2

>>> round(2.3 + n)
>>> 2

>>> round(2.4 + n)
>>> 2

>>> round(2.5 + n)
>>> 3

>>> round(2.6 + n)
>>> 3

>>> round(2.7 + n)
>>> 3

>>> round(2.8 + n)
>>> 3

>>> round(2.9 + n)
>>> 3

>>> round(3 + n)
>>> 3
于 2020-01-05T09:43:55.950 回答
-4

试试这个代码:

def roundup(input):   
   demo = input  if str(input)[-1] != "5" else str(input).replace("5","6")
   place = len(demo.split(".")[1])-1
   return(round(float(demo),place))

结果将是:

>>> x = roundup(2.5)
>>> x
3.0  
>>> x = roundup(2.05)
>>> x
2.1 
>>> x = roundup(2.005)
>>> x
2.01 

您可以在此处查看输出: https ://i.stack.imgur.com/QQUkS.png

于 2020-01-02T17:03:54.607 回答
-6

您可以使用 math.ceil 模块控制舍入:

import math
print(math.ceil(2.5))
> 3
于 2020-02-14T15:44:48.710 回答