【问题标题】:Round python decimal to nearest 0.05将python小数四舍五入到最接近的0.05
【发布时间】:2021-04-09 21:50:50
【问题描述】:

我正在尝试将十进制的货币数字四舍五入到最接近的 0.05。现在,我正在这样做:

def round_down(amount):
    amount *= 100
    amount = (amount - amount % 5) / Decimal(100)
    return Decimal(amount)

def round_up(amount):
    amount = int(math.ceil(float(100 * amount) / 5)) * 5 / Decimal(100)
    return Decimal(amount)

有没有什么方法可以让我更优雅地做到这一点,而无需使用 python Decimals(也许使用 quantize)处理浮点数?

【问题讨论】:

  • 为什么不在python中使用round?
  • @SeekAddo。我如何四舍五入到最接近的 0.05?我还需要控制舍入方向(即我需要向上/向下舍入)。
  • def round_num(amount): print(round(amount,2)) round_num(23.045954) 给你 23.05,我问一下,你希望如何向下取整像 @gurch101跨度>
  • @SeekAddo 我很确定以上两个函数可以正常工作。只是在寻找更优雅的解决方案。

标签: python rounding


【解决方案1】:

对于浮点数,只需使用round(x * 2, 1) / 2。但是,这并不能控制舍入方向。

使用Decimal.quantize,您还可以完全控制舍入的类型和方向(Python 3.5.1):

>>> from decimal import Decimal, ROUND_UP

>>> x = Decimal("3.426")
>>> (x * 2).quantize(Decimal('.1'), rounding=ROUND_UP) / 2
Decimal('3.45')

>>> x = Decimal("3.456")
>>> (x * 2).quantize(Decimal('.1'), rounding=ROUND_UP) / 2
Decimal('3.5')

【讨论】:

    【解决方案2】:

    任何舍入基础的更通用的解决方案。

    from decimal import ROUND_DOWN
    def round_decimal(decimal_number, base=1, rounding=ROUND_DOWN):
        """
        Round decimal number to the nearest base
    
        :param decimal_number: decimal number to round to the nearest base
        :type decimal_number: Decimal
        :param base: rounding base, e.g. 5, Decimal('0.05')
        :type base: int or Decimal
        :param rounding: Decimal rounding type
        :rtype: Decimal
        """
        return base * (decimal_number / base).quantize(1, rounding=rounding)
    

    例子:

    >>> from decimal import Decimal, ROUND_UP
    
    >>> round_decimal(Decimal('123.34'), base=5)
    Decimal('120')
    >>> round_decimal(Decimal('123.34'), base=6, rounding=ROUND_UP)
    Decimal('126')
    >>> round_decimal(Decimal('123.34'), base=Decimal('0.05'))
    Decimal('123.30')
    >>> round_decimal(Decimal('123.34'), base=Decimal('0.5'), rounding=ROUND_UP)
    Decimal('123.5')
    

    【讨论】:

    • 如果我打电话给round_decimal(Decimal('123.36'), base=Decimal('0.09')),我会得到Decimal('123.30')。我不应该得到Decimal('123.29')吗?我可能错过了什么。
    • 使用十进制基数时,它的工作方式就像您首先将 decimal_number 和基数乘以基数的最低有效数字“乘数”一样。 base = 0.09decimal_number = 123.36multiplier = 100然后base = 9decimal_numer = 1233612336 / 9 = 1370.6666666666666671370 * 9 = 1233012330 / 100 = 123.30
    【解决方案3】:

    首先注意这个问题(意外向下舍入)有时仅在您要舍入到的数字紧邻(左侧)的数字具有@时发生987654324@.

    >>> round(1.0005,3)
    1.0
    >>> round(2.0005,3)
    2.001
    >>> round(3.0005,3)
    3.001
    >>> round(4.0005,3)
    4.0
    >>> round(1.005,2)
    1.0
    >>> round(5.005,2)
    5.0
    >>> round(6.005,2)
    6.0
    >>> round(7.005,2)
    7.0
    >>> round(3.005,2)
    3.0
    >>> round(8.005,2)
    8.01
    

    但是有一个简单的解决方案,我发现它似乎总是有效,并且不依赖于其他库的import。解决方案是添加一个1e-X,其中X 是您尝试使用round 加上1 的数字字符串的长度。

    >>> round(0.075,2)
    
    0.07
    
    >>> round(0.075+10**(-2*6),2)
    
    0.08
    

    啊哈!所以基于此我们可以制作一个方便的包装函数,它是独立的,不需要额外的import调用...

    def roundTraditional(val,digits):
       return round(val+10**(-len(str(val))-1))
    

    基本上,这会添加一个保证小于您尝试使用round 的字符串的最小给定数字的值。通过添加少量,它在大多数情况下保留round 的行为,同时现在确保低于被舍入的数字是否为5,它会向上舍入,如果是4,它会向下舍入。

    使用10**(-len(val)-1) 的方法是经过深思熟虑的,因为它是您可以添加的最大的小数字来强制转换,同时还确保您添加的值永远不会改变舍入,即使小数点. 丢失。我可以只使用10**(-len(val)) 和条件if (val>1) 来减去1 更多...但是总是减去1 更简单,因为这不会改变这个解决方法可以正确地适用的十进制数范围处理。如果您的值达到类型的限制,此方法将失败,这将失败,但对于几乎整个有效十进制值范围,它应该可以工作。

    您也可以使用decimal 库来完成此操作,但我建议的包装器更简单,在某些情况下可能更受欢迎。


    编辑:感谢Blckknght 指出5 边缘情况仅适用于某些值here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多