舍入浮点数到x小数?

时间:2012-11-20 17:55:05

标签: python math rounding

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

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

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

5 个答案:

答案 0 :(得分:102)

我觉得有必要对Ashwini Chaudhary的回答提出一个对立面。尽管有外观,round函数的双参数形式不会将Python浮点数舍入到给定的小数位数,并且它通常不是您想要的解决方案,即使你认为它。让我解释一下......

将(Python)浮点数舍入到一定数量的小数位的能力是经常要求的,但事实证明很少是实际需要的。简单的回答round(x, number_of_places)是一个有吸引力的麻烦:它看起来就像它做你想要的那样,但是由于Python浮动内部存储在二进制中,它&#39 ;做一些相当微妙的事情。请考虑以下示例:

>>> round(52.15, 1)
52.1

round的作用有一种天真的理解,这看起来是错误的:当然应该将向上舍入到52.2而不是向下52.1?要理解为什么不能依赖这些行为,你需要意识到虽然这看起来像一个简单的十进制到十进制操作,但它远非简单。

所以,上面的示例中出现了真正的内容。 (深呼吸)我们将最近的二进制浮点数的十进制表示显示为最近的n -digits-after-the-point <点>十进制编号为二进制十进制编写的数字文字的浮点近似值。因此,为了从原始数字文字到显示的输出,底层机制在二进制和十进制格式之间进行了四个单独的转换,每个方向两个。打破它(与通常的免责声明关于假设IEEE 754二进制64格式,舍入到均匀舍入和IEEE 754规则):

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

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

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

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

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

    repr

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

    >>> x = 15.34509809234
    >>> x
    15.34509809234
    

    我们不仅向相反的方向前进,而是>>> round(52.15, 1) 52.200000000000003 而不是52.2,但显示的值甚至不会打印为52.1!这种行为导致了许多关于Python错误跟踪器的报告,并且#34;圆形被打破!&#34;。但它没有被52.2打破,这是用户的期望。 (好吧,好的,round是Python 2.6中的 little 位,因为它没有使用正确的舍入。)

    简短版本:如果您正在使用双参数回合,并且您期望从二进制近似到十进制轮的可预测行为一个二进制逼近十进制中途的情况,你要求麻烦。

    足够的&#34;两个参数回合是不好的&#34;论点。您应该 使用什么?根据您的尝试,有几种可能性。

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

      round

      即使这样,也必须注意内部二进制表示,以免被明显的十进制中间行为的行为感到惊讶。

      >>> format(66.66666666666, '.4f')
      '66.6667'
      >>> format(1.29578293, '.6f')
      '1.295783'
      
    • 如果您在关注十进制中途案例四舍五入的方向(例如,在某些财务环境中)中运作,您可能希望使用>>> format(52.15, '.1f') '52.1' 来表示您的数字类型。对Decimal类型进行十进制舍入比使用二进制类型更有意义(同样地,舍入到固定数量的二进制位置在二进制类型上非常有意义)。此外,Decimal模块可以更好地控制舍入模式。在Python 3中,decimal直接完成工作。在Python 2中,您需要round方法。

      quantize
    • 在极少数情况下,>>> Decimal('66.66666666666').quantize(Decimal('1e-4')) Decimal('66.6667') >>> Decimal('1.29578293').quantize(Decimal('1e-6')) Decimal('1.295783') 的双参数版本

      你并不特别关心边境案件的走向。然而,这些案例很少见,并且很难证明仅基于这些案例存在round内置的双参数版本。

答案 1 :(得分:55)

使用内置函数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]) - &gt;浮点数

     

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

答案 2 :(得分: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)))

(“值”到浮点数然后是字符串的转换非常重要,这样,“值”可以是浮点,小数,整数或字符串类型!)

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

答案 3 :(得分: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]

信用:kares

答案 4 :(得分: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