【问题标题】:Truncate to three decimals in Python在 Python 中截断为小数点后三位
【发布时间】:2011-12-21 20:31:09
【问题描述】:

我如何获得 1324343032.324?

如下所示,以下方法不起作用:

>>1324343032.324325235 * 1000 / 1000
1324343032.3243253
>>int(1324343032.324325235 * 1000) / 1000.0
1324343032.3239999
>>round(int(1324343032.324325235 * 1000) / 1000.0,3)
1324343032.3239999
>>str(1324343032.3239999)
'1324343032.32'

【问题讨论】:

  • 集合中没有这样的浮点数表示的值。
  • 如果 Karl 的评论不够清楚:二进制浮点中 没有像 1324343032.324 这样的数字。如果您切换到更高版本的 Python(2.7 或 3.1 或更高版本),解释器将为您显示 1324343032.324。但实际上,无论 Python 版本如何,您计算的数字既不是 1324343032.324 也不是 1324343032.3239999。获得准确 1324343032.324 的唯一方法是使用decimal 模块或其他一些任意精度的数学库,例如gmpy。
  • 如果您想四舍五入(向上)到给定的小数位数,下面接受的答案是正确的。但是,问题是什么,我想知道的是如何截断到特定的小数位数。对我来说,'%.3f'%(1324343032.3243) 和 '%.3f'%(1324343032.3245) 给出不同的结果。 (我使用的是 Python 2.7.8)。
  • @AbhranilDas 这个问题与您所指的问题重复。由于这两个问题的答案都具有误导性,因此我将其标记为重复,以便可以在一个地方处理与截断相关的所有问题。另外,请查看我的 cmets 和原始问题的建议答案。

标签: python


【解决方案1】:

如果您想将其保留为float,您可以在其周围使用额外的float()。

%.3f'%(1324343032.324325235)

【讨论】:

  • 这基本上是正确的答案,只需使用val = '%.3f'%(1324343032.324325235)而不是print。
  • 这个答案是正确的,如果你想四舍五入(向上)到给定的小数位数。然而,问题是什么,我想知道的是如何截断到特定的小数位数。对我来说,'%.3f'%(1324343032.3243) 和 '%.3f'%(1324343032.3245) 给出不同的结果。 (我使用的是 Python 2.7.8)。
  • '%.2f' % 8866.316 是圆的但不是截断的
  • 你正在四舍五入;看看我的回答
  • 这个它没有截断。
【解决方案2】:

您可以使用以下函数将数字截断为设置的小数位数:

import math
def truncate(number, digits) -> float:
    stepper = 10.0 ** digits
    return math.trunc(stepper * number) / stepper

用法:

>>> truncate(1324343032.324325235, 3)
1324343032.324

【讨论】:

  • 这确实应该是公认的答案。代码简单、优雅且最有意义。使用标准截断函数,但是首先移动小数位,一旦执行截断,将小数位向后移动。
  • 注意浮点精度错误truncate(-1.13, 2) == -1.12,我把它改了一下(口中有苦味)为math.trunc(round(stepper * number, digits * 3)) / stepper
  • 你可以只做 int(stepper * number) / stepper
【解决方案3】:

我找到了另一种解决方案(它必须比“字符串巫术”解决方法更有效):

>>> import decimal
# By default rounding setting in python is decimal.ROUND_HALF_EVEN
>>> decimal.getcontext().rounding = decimal.ROUND_DOWN
>>> c = decimal.Decimal(34.1499123)
# By default it should return 34.15 due to '99' after '34.14'
>>> round(c,2)
Decimal('34.14')
>>> float(round(c,2))
34.14
>>> print(round(c,2))
34.14

About decimals module

About rounding settings

【讨论】:

  • 我认为这应该是更被接受的答案之一......它教你如何利用和修改模块以发挥你的优势,它表达了 Python 的真正动态和强大的功能
  • @solveme 唯一的问题,我个人不知道为什么标准数学库中没有圆形。据报道,十进制库的精度非常高成本。仍然比直接上面的答案好 100 倍,后者会抛出一个数字太大的异常。干得好。
【解决方案4】:

这个怎么样:

In [1]: '%.3f' % round(1324343032.324325235 * 1000 / 1000,3)
Out[1]: '1324343032.324'

round() in Python doesn't seem to be rounding properly 可能重复

[编辑]

鉴于我相信你会想要做的额外的 cmets:

In : Decimal('%.3f' % (1324343032.324325235 * 1000 / 1000))
Out: Decimal('1324343032.324')

浮点精度不会是你想要的:

In : 3.324
Out: 3.3239999999999998

(所有示例均使用 Python 2.6.5)

【讨论】:

    【解决方案5】:

    '%.3f'%(1324343032.324325235)

    在这种特殊情况下没关系。

    只需稍微改变一下数字:

    1324343032.324725235

    然后:

    '%.3f'%(1324343032.324725235)
    

    给你1324343032.325

    试试这个:

    def trun_n_d(n,d):
        s=repr(n).split('.')
        if (len(s)==1):
            return int(s[0])
        return float(s[0]+'.'+s[1][:d])
    

    trun_n_d 的另一个选项:

    def trun_n_d(n,d):
        dp = repr(n).find('.') #dot position
        if dp == -1:  
            return int(n) 
        return float(repr(n)[:dp+d+1])
    

    trun_n_d 的另一个选项(一个 oneliner 选项)[这个,假设 'n' 是一个 str 和 'd' 是一个 int]:

    def trun_n_d(n,d):
        return (  n if not n.find('.')+1 else n[:n.find('.')+d+1]  )
    

    trun_n_d 在 Python 2.7 和 Python 3.6 中都为您提供所需的输出

    trun_n_d(1324343032.324325235,3) 返回 1324343032.324

    同样,trun_n_d(1324343032.324725235,3) 返回 1324343032.324


    注意 1 在 Python 3.6(可能还有 Python 3.x)中,类似这样的东西可以正常工作:

    def trun_n_d(n,d):
        return int(n*10**d)/10**d
    

    但是,这样一来,圆圆的幽灵总是潜伏在身边。

    注意 2 在这种情况下,由于 python 的数字内部结构,如舍入和缺乏精度,使用 n 作为str 比使用它的 int 对应物要好得多;你总是可以在最后将你的号码转换为 float。

    【讨论】:

      【解决方案6】:

      使用十进制模块。但是,如果您必须使用浮点数并且仍然以某种方式将它们强制转换为给定数量的小数点转换为字符串,那么 back 提供了一种(恐怕相当笨拙)的方法。

      >>> q = 1324343032.324325235 * 1000 / 1000
      >>> a = "%.3f" % q
      >>> a
      '1324343032.324'
      >>> b = float(a)
      >>> b
      1324343032.324
      

      所以:

      float("%3.f" % q)
      

      【讨论】:

      • 很高兴您首先提到了十进制模块,因为这是唯一完全正确的答案。需要注意的一件事是,在您的示例中,b 在 2.7 之前的 Python 版本上将显示为 1324343032.3239999。事实上,这就是 OP 在尝试时看到的价值。当然,就二进制浮点而言,这两个值是无法区分的。
      【解决方案7】:

      我认为使用format 函数是个坏主意。请看下文。它对值进行四舍五入。我使用 Python 3.6。

      >>> '%.3f'%(1.9999999)
      '2.000'
      

      改用正则表达式:

      >>> re.match(r'\d+.\d{3}', str(1.999999)).group(0)
      '1.999'
      

      【讨论】:

        【解决方案8】:

        Almo 的链接解释了为什么会发生这种情况。要解决此问题,请使用decimal library。

        【讨论】:

          【解决方案9】:

          也许这样:

          def myTrunc(theNumber, theDigits):
          
              myDigits = 10 ** theDigits
              return (int(theNumber * myDigits) / myDigits)
          

          【讨论】:

          • 请注意,这适用于 Python 3,但不适用于 Python 2,因为 / 的工作方式发生了变化。
          【解决方案10】:

          好的,这只是解决这个问题的另一种方法,将数字作为字符串处理并执行一个简单的切片。这会为您提供一个截断的数字输出,而不是四舍五入的输出。

          num = str(1324343032.324325235)
          i = num.index(".")
          truncated = num[:i + 4]
              
          print(truncated)
          

          输出:

          '1324343032.324'
          

          当然可以解析:

          float(truncated)
          

          【讨论】:

            【解决方案11】:

            功能

            def truncate(number: float, digits: int) -> float:
                pow10 = 10 ** digits
                return number * pow10 // 1 / pow10
            

            测试代码

            f1 = 1.2666666
            f2 = truncate(f1, 3)
            print(f1, f2)
            

            输出

            1.2666666 1.266
            

            解释

            它向左移动f1 数字digits 次,然后删除所有小数,最后向右移动数字digits 次。

            序列中的示例:

            1.2666666 # number
            1266.6666 # number * pow10
            1266.0    # number * pow10 // 1
            1.266     # number * pow10 // 1 / pow10
            

            【讨论】:

              【解决方案12】:

              在寻找解决此问题的方法后,无需加载任何 Python 3 模块或额外的数学运算,我仅使用 str.format() 和 .float() 解决了该问题。我认为这种方式比使用其他数学运算更快,比如在最常见的解决方案中。我需要一个快速的解决方案,因为我使用的是一个非常大的数据集,所以它在这里工作得很好。

              def truncate_number(f_number, n_decimals):
                    strFormNum = "{0:." + str(n_decimals+5) + "f}"
                    trunc_num = float(strFormNum.format(f_number)[:-5])
                    return(trunc_num)
              
              # Testing the 'trunc_num()' function
              test_num = 1150/252
              [(idx, truncate_number(test_num, idx)) for idx in range(0, 20)]
              

              它返回以下输出:

              [(0, 4.0),
               (1, 4.5),
               (2, 4.56),
               (3, 4.563),
               (4, 4.5634),
               (5, 4.56349),
               (6, 4.563492),
               (7, 4.563492),
               (8, 4.56349206),
               (9, 4.563492063),
               (10, 4.5634920634),
               (11, 4.56349206349),
               (12, 4.563492063492),
               (13, 4.563492063492),
               (14, 4.56349206349206),
               (15, 4.563492063492063),
               (16, 4.563492063492063),
               (17, 4.563492063492063),
               (18, 4.563492063492063),
               (19, 4.563492063492063)]
              

              【讨论】:

                【解决方案13】:

                我认为最好和正确的方法是使用decimal模块。

                import decimal
                
                a = 1324343032.324325235
                
                decimal_val = decimal.Decimal(str(a)).quantize(
                   decimal.Decimal('.001'), 
                   rounding=decimal.ROUND_DOWN
                )
                float_val = float(decimal_val)
                
                print(decimal_val)
                >>>1324343032.324
                
                print(float_val)
                >>>1324343032.324
                

                您可以为rounding=decimal.ROUND_DOWN 使用不同的值,可用选项有ROUND_CEILING、ROUND_DOWN、ROUND_FLOOR、ROUND_HALF_DOWN、ROUND_HALF_EVEN、ROUND_HALF_UP、ROUND_UP 和ROUND_05UP。您可以找到每个选项的说明here in docs。

                【讨论】:

                  【解决方案14】:

                  我建议下一个解决方案:

                  def my_floor(num, precision):
                     return f'{num:.{precision+1}f}'[:-1]
                  
                  my_floor(1.026456,2) # 1.02
                  
                  

                  【讨论】:

                  • 我喜欢这个,因为它使用了现代 Python 字符串插值。但我不明白为什么你把它的长度比需要的长,然后剪掉最后一个字符。这与舍入无关,因为您的解决方案执行 OP 要求的截断,而不是舍入(如您的示例所示)。
                  【解决方案15】:

                  你也可以使用:

                  import math
                  
                  nValeur = format(float(input('Quelle valeur ?    ')), '.3f')
                  

                  在 Python 3.6 中它可以工作。

                  【讨论】:

                    【解决方案16】:
                    a = 1.0123456789
                    dec = 3 # keep this many decimals
                    p = 10 # raise 10 to this power
                    a * 10 ** p // 10 ** (p - dec) / 10 ** dec
                    >>> 1.012
                    

                    【讨论】:

                      【解决方案17】:

                      也许python在这个问题之后发生了变化,下面所有的似乎都运行良好

                      Python2.7

                      int(1324343032.324325235 * 1000) / 1000.0
                      float(int(1324343032.324325235 * 1000)) / 1000
                      round(int(1324343032.324325235 * 1000) / 1000.0,3)
                      # result for all of the above is 1324343032.324
                      

                      【讨论】:

                        【解决方案18】:

                        我正在尝试生成一个介于 5 到 7 之间的随机数,并希望将其限制为小数点后 3 位。

                        import random
                        
                        num = float('%.3f' % random.uniform(5, 7))
                        print (num)
                        

                        【讨论】:

                          【解决方案19】:

                          我开发了一个很好的解决方案,我知道有很多 If 声明,但它有效! (仅适用于

                          def truncate(number, digits) -> float:
                              startCounting = False
                              if number < 1:
                                number_str = str('{:.20f}'.format(number))
                                resp = ''
                                count_digits = 0
                                for i in range(0, len(number_str)):
                                  if number_str[i] != '0' and number_str[i] != '.' and number_str[i] != ',':
                                    startCounting = True
                                  if startCounting:
                                    count_digits = count_digits + 1
                                  resp = resp + number_str[i]
                                  if count_digits == digits:
                                      break
                                return resp
                              else:
                                return number
                          

                          【讨论】:

                            【解决方案20】:

                            基于@solveMe asnwer (https://stackoverflow.com/a/39165933/641263),我认为这是利用十进制上下文最正确的方法之一,我创建了以下完全根据需要完成工作的方法:

                            import decimal
                            
                            def truncate_decimal(dec: Decimal, digits: int) -> decimal.Decimal:
                                round_down_ctx = decimal.getcontext()
                                round_down_ctx.rounding = decimal.ROUND_DOWN
                                new_dec = round_down_ctx.create_decimal(dec)
                                return round(new_dec, digits)
                            

                            【讨论】:

                              【解决方案21】:
                              >>> float(1324343032.324325235) * float(1000) / float(1000)
                              
                              1324343032.3243253
                              
                              >>> round(float(1324343032.324325235) * float(1000) / float(1000), 3)
                              
                              1324343032.324
                              

                              【讨论】:

                              • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
                              猜你喜欢
                              • 2014-01-02
                              • 2021-08-24
                              • 1970-01-01
                              • 2017-07-04
                              • 2019-04-23
                              • 2018-03-17
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多