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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33019698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 12:37:27  来源:igfitidea点击:

How to properly round up half float numbers in Python?

pythonpython-3.xfloating-pointroundingprecision

提问by Delgan

I am facing a strange behavior of the round()function:

我正面临该round()函数的奇怪行为:

for i in range(1, 15, 2):
    n = i / 2
    print(n, "=>", round(n))

This code prints:

此代码打印:

0.5 => 0
1.5 => 2
2.5 => 2
3.5 => 4
4.5 => 4
5.5 => 6
6.5 => 6

I expected the floating values to be always rounded up, but instead, it is rounded to the nearest even number.

我希望浮动值总是四舍五入,但相反,它四舍五入到最接近的偶数。

Why such behavior, and what is the best way to get the correct result?

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

I tried to use the fractionsbut the result is the same.

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

采纳答案by Martijn Pieters

The Numeric Typessectiondocuments this behaviour explicitly:

数值类型部分文件这种行为明确:

round(x[, n])
xrounded to n digits, rounding half to even. If n is omitted, it defaults to 0.

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

Note the rounding half to even. This is also called bankers rounding; instead of always rounding up or down (compounding rounding errors), by rounding to the nearest evennumber you average out rounding errors.

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

If you need more control over the rounding behaviour, use the decimalmodule, which lets you specify exactly what rounding strategy should be used.

如果您需要更多地控制舍入行为,请使用decimal模块,它可以让您准确指定应使用的舍入策略

For example, to round up from half:

例如,从一半向上取整:

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

回答by MRocklin

Rounding to the nearest even number has become common practice in numerical disciplines. "Rounding up" produces a slight bias towards larger results.

四舍五入到最接近的偶数已成为数值学科的常见做法。“四舍五入”会对更大的结果产生轻微的偏见。

So, from the perspective of the scientific establishment, roundhas the correct behavior.

所以,从科学的角度来看,round具有正确的行为。

回答by rmunn

Short version: use the decimal module. It can represent numbers like 2.675 precisely, unlike Python floats where 2.675 is really2.67499999999999982236431605997495353221893310546875 (exactly). And you can specify the rounding you desire: ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP, and ROUND_05UP are all options.

简短版本:使用小数模块。它可以精确地表示像 2.675 这样的数字,不像 Python 浮点数,其中 2.675实际上是2.67499999999999982236431605997495353221893310546875(完全正确)。并且您可以指定您想要的舍入:ROUND_CEILING、ROUND_DOWN、ROUND_FLOOR、ROUND_HALF_DOWN、ROUND_HALF_EVEN、ROUND_HALF_UP、ROUND_UP 和 ROUND_05UP 都是选项。

回答by Patricia Shanahan

The behavior you are seeing is typical IEEE 754 rounding behavior. If it has to choose between two numbers that are equally different from the input, it always picks the even one. The advantage of this behavior is that the average rounding effect is zero - equally many numbers round up and down. If you round the half way numbers in a consistent direction the rounding will affect the expected value.

您看到的行为是典型的 IEEE 754 舍入行为。如果它必须在与输入相同的两个数字之间进行选择,它总是选择偶数。这种行为的优点是平均舍入效果为零 - 上下舍入的数字相等。如果您以一致的方向将中间数字舍入,则舍入将影响预期值。

The behavior you are seeing is correct if the objective is fair rounding, but that is not always what is needed.

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

One trick to get the type of rounding you want is to add 0.5 and then take the floor. For example, adding 0.5 to 2.5 gives 3, with floor 3.

获得您想要的舍入类型的一个技巧是添加 0.5,然后取舍。例如,将 0.5 与 2.5 相加得到 3,层数为 3。

回答by dhobbs

For example:

例如:

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)

回答by cdonts

You can use:

您可以使用:

from decimal import Decimal, ROUND_HALF_UP

for i in range(1, 15, 2):
    n = i / 2
    print(n, "=>", Decimal(str(n)).quantize(Decimal("1"), rounding=ROUND_HALF_UP))

回答by fedor2612

You can use this:

你可以使用这个:

import math
def normal_round(n):
    if n - math.floor(n) < 0.5:
        return math.floor(n)
    return math.ceil(n)

It will round number up or down properly.

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

回答by Matthew D. Scholefield

round()will round either up or down, depending on if the number is even or odd. A simple way to only round up is:

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

int(num + 0.5)

If you want this to work properly for negative numbers use:

如果您希望它对负数正常工作,请使用:

((num > 0) - (num < 0)) * int(abs(num) + 0.5)

Note, this can mess up for large numbers or really precise numbers like 5000000000000001.0and 0.49999999999999994.

请注意,这可能会弄乱大数字或真正精确的数字,例如5000000000000001.00.49999999999999994

回答by Joe Cat

Love the fedor2612answer. I expanded it with an optional "decimals" argument for those who want to use this function to round any number of decimals (say for example if you want to round a currency $26.455 to $26.46).

喜欢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)

Output:

输出:

26.45

26.46

26.45

26.46

回答by discover

Here is another solution. It will work as normal rounding in excel.

这是另一种解决方案。它将在 excel 中正常四舍五入。

from decimal import Decimal, getcontext, ROUND_HALF_UP

round_context = getcontext()
round_context.rounding = ROUND_HALF_UP

def c_round(x, digits, precision=5):
    tmp = round(Decimal(x), precision)
    return float(tmp.__round__(digits))

c_round(0.15, 1) -> 0.2, c_round(0.5, 0) -> 1

c_round(0.15, 1) -> 0.2, c_round(0.5, 0) -> 1