97

有没有办法将 python 浮点数舍入到 x 小数?例如:

>>> x = roundfloat(66.66666666666, 4)
66.6667
>>> x = roundfloat(1.29578293, 6)
1.295783

我找到了修剪/截断它们的方法(66.666666666 --> 66.6666),但不是圆形(66.666666666 --> 66.6667)。

4

5 回答 5

150

我觉得有必要对 Ashwini Chaudhary 的回答提出反驳。round尽管看起来,该函数的两个参数形式不会将 Python 浮点数四舍五入到给定的小数位数,而且它通常不是您想要的解决方案,即使您认为它是。让我解释...

将(Python)浮点数四舍五入到小数位数的能力是经常要求的,但实际上很少需要。令人着迷的简单答案round(x, number_of_places)有点令人讨厌:它看起来好像在做你想做的事,但由于 Python 浮点数在内部以二进制形式存储,它做的事情相当微妙。考虑以下示例:

>>> round(52.15, 1)
52.1

天真地理解是什么round,这看起来是错误的:当然它应该向上52.2取整而不是向下取整到52.1? 要理解为什么不能依赖这样的行为,您需要了解虽然这看起来像是一个简单的十进制到十进制运算,但它远非简单。

所以这就是上面例子中真正发生的事情。(深呼吸)我们将最接近的二进制浮点数的十进制表示显示为最接近的点后数字十进制数到以十进制编写的数字文字的二进制浮点近似值。因此,为了从原始数字文字到显示的输出,底层机器在二进制和十进制格式之间进行了四次单独的转换,每个方向两次。将其分解(以及关于假设 IEEE 754 binary64 格式、舍入到偶数舍入和 IEEE 754 规则的常见免责声明):n

  1. 首先,数字文字52.15被解析并转换为 Python 浮点数。实际存储的数字是7339460017730355 * 2**-4752.14999999999999857891452847979962825775146484375

  2. 作为操作的第一步round,Python 在内部计算最接近存储数字的点后 1 位十进制字符串。由于存储的数字略低于 的原始值52.15,因此我们最终向下舍入并得到一个字符串52.1。这解释了为什么我们得到52.1的是最终输出而不是52.2.

  3. 然后在操作的第二步中round,Python 将该字符串转换回浮点数,得到最接近的二进制浮点数52.1,即现在7332423143312589 * 2**-47,或52.10000000000000142108547152020037174224853515625

  4. 最后,作为 Python 的 read-eval-print 循环 (REPL) 的一部分,将显示浮点值(十进制)。这涉及将二进制值转换回十进制字符串,52.1作为最终输出。

在 Python 2.7 及更高版本中,我们遇到了令人愉快的情况,即第 3 步和第 4 步中的两次转换相互抵消。这是由于 Python 选择的repr实现,它产生保证正确舍入到实际浮点数的最短十进制值。该选择的一个后果是,如果您从任何(不太大,也不太小)具有 15 个或更少有效数字的十进制文字开始,则将显示相应的浮点数,显示这些完全相同的数字:

>>> x = 15.34509809234
>>> x
15.34509809234

不幸的是,这进一步加深了 Python 以十进制存储值的错觉。但在 Python 2.6 中并非如此!这是在 Python 2.6 中执行的原始示例:

>>> round(52.15, 1)
52.200000000000003

我们不仅朝相反的方向四舍五入,得到52.2而不是52.1,而且显示的值甚至不打印为52.2!此行为已导致向 Python 错误跟踪器报告大量报告,如“圆坏了!”。但这并不是round破坏了,而是用户的期望。(好吧,好吧,在 Python 2.6round有点破,因为它没有使用正确的舍入。)

简短版本:如果您使用双参数回合,并且您期望从二进制近似到十进制循环的可预测行为,二进制近似到十进制中途情况,那么您就是在自找麻烦。

“两轮论证不好”的论点就足够了。你应该改用什么?有几种可能性,具体取决于您要执行的操作。

  • 如果您出于显示目的四舍五入,那么您根本不需要浮点结果;你想要一个字符串。在这种情况下,答案是使用字符串格式:

    >>> format(66.66666666666, '.4f')
    '66.6667'
    >>> format(1.29578293, '.6f')
    '1.295783'
    

    即使这样,也必须了解内部二进制表示,以免对明显的十进制中途情况的行为感到惊讶。

    >>> format(52.15, '.1f')
    '52.1'
    
  • 如果您在一个很重要的上下文中操作,其中小数中位数的四舍五入很重要(例如,在某些金融环境中),您可能希望使用该Decimal类型来表示您的数字。对类型进行十进制四舍五入Decimal比对二进制类型更有意义(同样,四舍五入到固定数量的二进制位对二进制类型非常有意义)。此外,该decimal模块使您可以更好地控制舍入模式。在 Python 3 中,round直接完成这项工作。在 Python 2 中,您需要该quantize方法。

    >>> Decimal('66.66666666666').quantize(Decimal('1e-4'))
    Decimal('66.6667')
    >>> Decimal('1.29578293').quantize(Decimal('1e-6'))
    Decimal('1.295783')
    
  • round在极少数情况下,真的你想要的两个参数版本:也许你正在将浮动分箱到 size 的箱中0.01,而你并不特别关心边界情况的走向。但是,这些情况很少见,仅基于这些情况很难证明存在两个参数版本的round内置函数。

于 2014-03-03T19:43:37.403 回答
105

使用内置函数round()

In [23]: round(66.66666666666,4)
Out[23]: 66.6667

In [24]: round(1.29578293,6)
Out[24]: 1.295783

帮助round()

round(number[, ndigits]) -> 浮点数

将数字四舍五入到十进制数字的给定精度(默认为 0 位)。这总是返回一个浮点数。精度可能为负。

于 2012-11-20T17:56:40.630 回答
0

Mark Dickinson 的答案虽然完整,但不适用于 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!)

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

于 2020-07-22T13:40:34.817 回答
0

python和numpy中的默认舍入:

In: [round(i) for i in np.arange(10) + .5]
Out: [0, 2, 2, 4, 4, 6, 6, 8, 8, 10]

我用它来获得整数舍入以应用于熊猫系列:

import decimal

并使用这条线将四舍五入设置为“一半”,也就是学校教的四舍五入: decimal.getcontext().rounding = decimal.ROUND_HALF_UP

最后我做了这个函数来将它应用到熊猫系列对象

def roundint(value):
    return value.apply(lambda x: int(decimal.Decimal(x).to_integral_value()))

所以现在你可以做roundint(df.columnname)

对于数字:

In: [int(decimal.Decimal(i).to_integral_value()) for i in np.arange(10) + .5]
Out: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

信用:卡雷斯

于 2020-08-04T19:21:07.930 回答
0

我编写了一个函数(在 Django 项目中用于 DecimalField),但它可以在 Python 项目中使用:

这段代码:

  • 管理整数位以避免数字太大
  • 管理小数位以避免数字太低
  • 管理有符号和无符号号码

带有测试的代码:

def convert_decimal_to_right(value, max_digits, decimal_places, signed=True):

    integer_digits = max_digits - decimal_places
    max_value = float((10**integer_digits)-float(float(1)/float((10**decimal_places))))

    if signed:
        min_value = max_value*-1
    else:
        min_value = 0

    if value > max_value:
        value = max_value

    if value < min_value:
        value = min_value

    return round(value, decimal_places)


value = 12.12345
nb = convert_decimal_to_right(value, 4, 2)
# nb : 12.12

value = 12.126
nb = convert_decimal_to_right(value, 4, 2)
# nb : 12.13

value = 1234.123
nb = convert_decimal_to_right(value, 4, 2)
# nb : 99.99

value = -1234.123
nb = convert_decimal_to_right(value, 4, 2)
# nb : -99.99

value = -1234.123
nb = convert_decimal_to_right(value, 4, 2, signed = False)
# nb : 0

value = 12.123
nb = convert_decimal_to_right(value, 8, 4)
# nb : 12.123
于 2021-02-26T12:14:17.870 回答