Python 3.x舍入行为

Lev*_*von 157 python rounding python-3.x

我刚刚重新阅读Python 3.0中的新功能,它指出:

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

圆形文件:

对于支持round()的内置类型,值被舍入到10的最接近的倍数到幂减去n; 如果两个倍数相等,则向均匀选择进行舍入

所以,在v2.7.3下:

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

In [86]: round(3.5)
Out[86]: 4.0
Run Code Online (Sandbox Code Playgroud)

正如我所料.但是,现在在v3.2.3下:

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

In [33]: round(3.5)
Out[33]: 4
Run Code Online (Sandbox Code Playgroud)

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

  1. 有没有人能够深入了解为什么会改变这种情况?
  2. 有没有其他主流编程语言(例如,C,C++,Java,Perl, ..)这样做(对我不一致)舍入?

我在这里错过了什么?

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

kin*_*all 141

Python 3.0的方式现在被认为是标准的舍入方法,尽管有些语言实现还没有在公共汽车上.

简单的"始终为圆形0.5向上"技术导致对较高数字的轻微偏差.通过大量计算,这可能很重要.Python 3.0方法消除了这个问题.

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

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

  • 我添加了一些关于AppleScript处理此问题的内容,因为我喜欢以"讽刺"的方式实现"旧"行为. (14认同)
  • ruby为`2.5.round`返回`3` (5认同)
  • 我现在还没有意识到这种"默认的标准舍入方法",您(或其他任何人)是否会知道C/C++/Java/Perl或任何其他"主流"语言是否以相同的方式实现舍入? (4认同)
  • Ruby做到了.微软的.NET语言可以做到这一点.但是,Java似乎并不存在.我不能追踪每种可能的语言,但我想这在最近设计的语言中最常见.我想C和C++已经足够老了,他们却没有. (3认同)
  • @kindall自1985年(IEEE 754-1985发布)以来,这种方法一直是IEEE默认的舍入模式.它也是C中的默认舍入模式,因为至少C89(因此也是C++),***,因为C99(以及之前有零星支持的C++ 11)"round()"函数已经可用而是使用从零开始的联系.内部浮点舍入和rint()系列函数仍遵循舍入模式设置,默认为将连接舍入为偶数. (2认同)
  • 我认为很明显这是一个文化多元化的主题,任何语言都不应该实现“默认”round(),除非它们明确使用标志或它的多个版本来改变其行为以适应每个学派的行为(例如对我来说,2.5 不变成 3)在学术和科学领域是闻所未闻的。 (2认同)

daw*_*awg 35

您可以使用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')
Run Code Online (Sandbox Code Playgroud)

  • 这正是我今天要寻找的。在 Python 3.4.3 中按预期工作。另外值得注意的是,如果您想舍入到最接近的 100(例如金钱),您可以通过将 quantize(decimal.Decimal('1')` 更改为 quantize(decimal.Decimal('0.00')) 来控制舍入量。 (3认同)
  • @Levon:常量 `ROUND_HALF_UP` 与 Python 2.X 的旧行为相同。 (2认同)
  • 您还可以为Decimal模块设置一个上下文,隐式地为您执行此操作.参见`setcontext()`函数. (2认同)
  • 只要“ndigits”为正,此解决方案就可以替代“round(number, ndigits)”,但令人烦恼的是,您不能使用它来替换“round(5, -1)”之类的内容。 (2认同)

小智 12

只是在这里添加一份文档中的重要说明:

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)
Run Code Online (Sandbox Code Playgroud)

  • @Adam:我觉得你很误.用于存储浮点数的二进制格式(IEEE 754 binary64)不能完全代表"2.675":计算机最接近的格式是"2.67499999999999982236431605997495353221893310546875".这非常接近,但它并不*完全*等于'2.675`:它非常轻微*接近2.67`而不是'2.68`.因此`round`函数做正确的事情,并将其舍入到更接近的2位数后点值,即2.67`.这与Python无关,而且与二进制浮点有关. (8认同)
  • 它不是"正确的东西",因为它被赋予了源代码常量:),但我明白你的观点. (2认同)

kar*_*res 9

Python 3.x 将 .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
Run Code Online (Sandbox Code Playgroud)

但是,如果需要,可以将小数舍入“返回”更改为始终向上舍入 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
Run Code Online (Sandbox Code Playgroud)


nar*_*nie 6

我最近也有这个问题。因此,我开发了一个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)
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助,

纳妮