代码之家  ›  专栏  ›  技术社区  ›  Peter Graham

在Python中如何将数字四舍五入到有效数字

  •  113
  • Peter Graham  · 技术社区  · 15 年前

    1234->1000

    0.12->0.1

    0.012->0.01

    6253->6000

    1999年->2000

    15 回复  |  直到 15 年前
        1
  •  156
  •   Tobias Kienzler    9 年前

    >>> round(1234, -3)
    1000.0
    

    因此,如果您只需要最有效的数字:

    >>> from math import log10, floor
    >>> def round_to_1(x):
    ...   return round(x, -int(floor(log10(abs(x)))))
    ... 
    >>> round_to_1(0.0232)
    0.02
    >>> round_to_1(1234243)
    1000000.0
    >>> round_to_1(13)
    10.0
    >>> round_to_1(4)
    4.0
    >>> round_to_1(19)
    20.0
    

    如果float大于1,您可能需要注意将其转换为整数。

        2
  •  106
  •   Peter Graham    15 年前

    %字符串格式中的g将格式化一个四舍五入到一些有效数字的浮点。它有时会使用“e”科学表示法,因此将舍入的字符串转换回浮点数,然后通过%s字符串格式。

    >>> '%s' % float('%.1g' % 1234)
    '1000'
    >>> '%s' % float('%.1g' % 0.12)
    '0.1'
    >>> '%s' % float('%.1g' % 0.012)
    '0.01'
    >>> '%s' % float('%.1g' % 0.062)
    '0.06'
    >>> '%s' % float('%.1g' % 6253)
    '6000.0'
    >>> '%s' % float('%.1g' % 1999)
    '2000.0'
    
        3
  •  60
  •   Stephen Rauch Afsar Ali    8 年前

    >>> from math import log10, floor
    >>> def round_sig(x, sig=2):
    ...   return round(x, sig-int(floor(log10(abs(x))))-1)
    ... 
    >>> round_sig(0.0232)
    0.023
    >>> round_sig(0.0232, 1)
    0.02
    >>> round_sig(1234243, 3)
    1230000.0
    
        4
  •  37
  •   Andy Jones    5 年前
    f'{float(f"{i:.1g}"):g}'
    # Or with Python <3.6,
    '{:g}'.format(float('{:.1g}'.format(i)))
    

    此解决方案与所有其他解决方案不同,因为:

    1. 解决OP问题
    2. 是的 额外包装
    3. 是的 需要任何用户定义的 辅助功能

    对于任意数 n

    print('{:g}'.format(float('{:.{p}g}'.format(i, p=n))))
    

    测试:

    a = [1234, 0.12, 0.012, 0.062, 6253, 1999, -3.14, 0., -48.01, 0.75]
    b = ['{:g}'.format(float('{:.1g}'.format(i))) for i in a]
    # b == ['1000', '0.1', '0.01', '0.06', '6000', '2000', '-3', '0', '-50', '0.8']
    

    注意 :使用此解决方案,无法从输入中动态调整有效数字的数量,因为没有标准方法来区分具有不同尾随零数的数字( 3.14 == 3.1400 ). 如果需要这样做,那么可以使用 to-precision

        5
  •  11
  •   offby1    7 年前

    我已经创建了包 to-precision 那正是你想要的。它允许你给你的数字或多或少有意义的数字。

    在公认的答案中有一行

    >>> round_to_1(1234243)
    1000000.0
    

    >>> from to_precision import to_precision
    >>> to_precision(1234243, 1, 'std')
    '1000000'
    >>> to_precision(1234243, 1, 'sci')
    '1e6'
    >>> to_precision(1234243, 1, 'eng')
    '1e6'
    

    它还将舍入最后一个有效数字,并可以在未指定符号时自动选择要使用的符号:

    >>> to_precision(599, 2)
    '600'
    >>> to_precision(1164, 2)
    '1.2e3'
    
        6
  •  7
  •   Sam Mason    6 年前

    为了直接回答这个问题,下面是我使用 R function

    import math
    
    def signif(x, digits=6):
        if x == 0 or not math.isfinite(x):
            return x
        digits -= math.ceil(math.log10(abs(x)))
        return round(x, digits)
    

    我发布这个答案的主要原因是有评论抱怨说“0.075”是0.07而不是0.08。正如“新手C”所指出的,这是由于浮点运算同时具有这两种功能 finite precision and a base-2 representation . 实际上可以表示的最接近0.075的数字稍微小一些,因此舍入结果与您可能天真地期望的结果不同。

    还要注意,这适用于任何非十进制浮点运算的使用,例如C和Java都有相同的问题。

    0.075.hex()
    

    这给了我们: 0x1.3333333333333p-4 Python docs C standard .

    为了展示这些数字是如何工作的,我们可以通过以下步骤回到起点:

    0x13333333333333 / 16**13 * 2**-4
    

    应该打印出来 0.075 . 16**13 是因为小数点后有13个十六进制数字 2**-4

    现在我们知道了浮点数是如何表示的,我们可以使用 decimal 模块给我们一些更精确的信息,告诉我们发生了什么:

    from decimal import Decimal
    
    Decimal(0x13333333333333) / 16**13 / 2**4
    

    给: 0.07499999999999999722444243844 round(0.075, 2) 0.07

        7
  •  5
  •   Cris Stringfellow    13 年前

    要将整数四舍五入为1位有效数字,基本思想是将其转换为一个浮点数,该浮点数前有1位数字,然后将其四舍五入,然后将其转换回原始整数大小。

    要做到这一点,我们需要知道10的最大幂小于整数。我们可以使用log10函数的floor来实现这个。

    from math import log10, floor
    def round_int(i,places):
        if i == 0:
            return 0
        isign = i/abs(i)
        i = abs(i)
        if i < 1:
            return 0
        max10exp = floor(log10(i))
        if max10exp+1 < places:
            return i
        sig10pow = 10**(max10exp-places+1)
        floated = i*1.0/sig10pow
        defloated = round(floated)*sig10pow
        return int(defloated*isign)
    
        8
  •  4
  •   calvinkrishy    9 年前
    def round_to_n(x, n):
        if not x: return 0
        power = -int(math.floor(math.log10(abs(x)))) + (n - 1)
        factor = (10 ** power)
        return round(x * factor) / factor
    
    round_to_n(0.075, 1)      # 0.08
    round_to_n(0, 1)          # 0
    round_to_n(-1e15 - 1, 16) # 1000000000000001.0
    

    希望能从上面所有的答案中选一个最好的(减去把它作为一行lambda;)。尚未探索,请随意编辑以下答案:

    round_to_n(1e15 + 1, 11)  # 999999999999999.9
    
        9
  •  4
  •   ryan281    6 年前

    我修改了indgar的解决方案来处理负数和小数字(包括零)。

    from math import log10, floor
    def round_sig(x, sig=6, small_value=1.0e-9):
        return round(x, sig - int(floor(log10(max(abs(x), abs(small_value))))) - 1)
    
        10
  •  3
  •   getting_sleepy    7 年前

    如果您想在不涉及字符串的情况下进行舍入,我在上面的注释中找到了链接:

    http://code.activestate.com/lists/python-tutor/70739/

    我觉得这是最好的。然后,当您使用任何字符串格式描述符打印时,您将得到合理的输出,并且您可以将数字表示用于其他计算目的。

    sys.float_info.min . 完整的解决方案是:

    import sys,math
    
    def tidy(x, n):
    """Return 'x' rounded to 'n' significant digits."""
    y=abs(x)
    if y <= sys.float_info.min: return 0.0
    return round( x, int( n-math.ceil(math.log10(y)) ) )
    

    float 如果你因为某种原因需要改变反应。实际上,你可以把限制推到:

    sys.float_info.min*sys.float_info.epsilon
    

    如果出于某种原因,您使用的是极小值,则不会引发错误。

        11
  •  2
  •   Tim McNamara    15 年前

    我想不出有什么东西能在开箱即用。但对于浮点数来说,它处理得相当好。

    >>> round(1.2322, 2)
    1.23
    

    或整数:

    >>> def intround(n, sigfigs):
    ...   n = str(n)
    ...   return n[:sigfigs] + ('0' * (len(n)-(sigfigs)))
    
    >>> intround(1234, 1)
    '1000'
    >>> intround(1234, 2)
    

    如果您想创建一个处理任意数字的函数,我的首选方法是将它们都转换为字符串,并查找一个小数位来决定要执行的操作:

    >>> def roundall1(n, sigfigs):
    ...   n = str(n)
    ...   try:
    ...     sigfigs = n.index('.')
    ...   except ValueError:
    ...     pass
    ...   return intround(n, sigfigs)
    

    另一个选择是检查类型。这将远远不够灵活,可能无法很好地与其他数字,如 Decimal 物体:

    >>> def roundall2(n, sigfigs):
    ...   if type(n) is int: return intround(n, sigfigs)
    ...   else: return round(n, sigfigs)
    
        12
  •  2
  •   Autumn    5 年前

    numpy.format_float_positional x 格式为4位有效数字,禁止使用科学符号。

    import numpy as np
    x=12345.6
    np.format_float_positional(x, precision=4, unique=False, fractional=False, trim='k')
    > 12340.
    
        13
  •  1
  •   eddygeek    7 年前

    使用python 2.6+ new-style formatting

    >>> "{0}".format(float("{0:.1g}".format(1216)))
    '1000.0'
    >>> "{0}".format(float("{0:.1g}".format(0.00356)))
    '0.004'
    

    在python2.7+中,可以省略前导字符 0

        14
  •  0
  •   Miriam Farber    8 年前

    我也遇到过这种情况,但我需要控制舍入类型。因此,我编写了一个快速函数(见下面的代码),可以考虑值、舍入类型和所需的有效数字。

    import decimal
    from math import log10, floor
    
    def myrounding(value , roundstyle='ROUND_HALF_UP',sig = 3):
        roundstyles = [ 'ROUND_05UP','ROUND_DOWN','ROUND_HALF_DOWN','ROUND_HALF_UP','ROUND_CEILING','ROUND_FLOOR','ROUND_HALF_EVEN','ROUND_UP']
    
        power =  -1 * floor(log10(abs(value)))
        value = '{0:f}'.format(value) #format value to string to prevent float conversion issues
        divided = Decimal(value) * (Decimal('10.0')**power) 
        roundto = Decimal('10.0')**(-sig+1)
        if roundstyle not in roundstyles:
            print('roundstyle must be in list:', roundstyles) ## Could thrown an exception here if you want.
        return_val = decimal.Decimal(divided).quantize(roundto,rounding=roundstyle)*(decimal.Decimal(10.0)**-power)
        nozero = ('{0:f}'.format(return_val)).rstrip('0').rstrip('.') # strips out trailing 0 and .
        return decimal.Decimal(nozero)
    
    
    for x in list(map(float, '-1.234 1.2345 0.03 -90.25 90.34543 9123.3 111'.split())):
        print (x, 'rounded UP: ',myrounding(x,'ROUND_UP',3))
        print (x, 'rounded normal: ',myrounding(x,sig=3))
    
        15
  •  0
  •   Ettore Galli    7 年前

    def smart_round(x, decimal_positions):
        dp = - int(math.log10(abs(x))) if x != 0.0 else int(0)
        return round(float(x), decimal_positions + dp if dp > 0 else decimal_positions)
    

    希望有帮助。

        16
  •  0
  •   Don Mclachlan    6 年前

    https://stackoverflow.com/users/1391441/gabriel ,以下内容是否解决了您对rnd(.075,1)的担忧? 警告:以浮点形式返回值

    def round_to_n(x, n):
        fmt = '{:1.' + str(n) + 'e}'    # gives 1.n figures
        p = fmt.format(x).split('e')    # get mantissa and exponent
                                        # round "extra" figure off mantissa
        p[0] = str(round(float(p[0]) * 10**(n-1)) / 10**(n-1))
        return float(p[0] + 'e' + p[1]) # convert str to float
    
    >>> round_to_n(750, 2)
    750.0
    >>> round_to_n(750, 1)
    800.0
    >>> round_to_n(.0750, 2)
    0.075
    >>> round_to_n(.0750, 1)
    0.08
    >>> math.pi
    3.141592653589793
    >>> round_to_n(math.pi, 7)
    3.141593
    
        17
  •  0
  •   Gnubie    6 年前

    这将返回一个字符串,以便正确显示不带小数部分的结果和以E表示法显示的较小值:

    def sigfig(x, num_sigfig):
        num_decplace = num_sigfig - int(math.floor(math.log10(abs(x)))) - 1
        return '%.*f' % (num_decplace, round(x, num_decplace))
    
        18
  •  0
  •   zephyr    6 年前

    既然一个问题回答得这么透彻,为什么不再加一个呢

    这更符合我的审美观,尽管上面的很多都是可比的

    import numpy as np
    
    number=-456.789
    significantFigures=4
    
    roundingFactor=significantFigures - int(np.floor(np.log10(np.abs(number)))) - 1
    rounded=np.round(number, roundingFactor)
    
    string=rounded.astype(str)
    
    print(string)
    

    这适用于单个数字和numpy数组,对于负数应该可以正常工作。

    if roundingFactor<=0:
        rounded=rounded.astype(int)
    

    不幸的是,这最后一步对于一组数字是行不通的——亲爱的读者,如果你需要的话,我将把它留给你去弄清楚。

        19
  •  0
  •   HyperActive    5 年前

    sigfig installing 您可以执行以下操作:

    >>> from sigfig import round
    >>> round(1234, 1)
    1000
    >>> round(0.12, 1)
    0.1
    >>> round(0.012, 1)
    0.01
    >>> round(0.062, 1)
    0.06
    >>> round(6253, 1)
    6000
    >>> round(1999, 1)
    2000
    
        20
  •  0
  •   LetzerWille    5 年前
    import math
    
      def sig_dig(x, n_sig_dig):
          num_of_digits = len(str(x).replace(".", ""))
          if n_sig_dig >= num_of_digits:
              return x
          n = math.floor(math.log10(x) + 1 - n_sig_dig)
          result = round(10 ** -n * x) * 10 ** n
          return float(str(result)[: n_sig_dig + 1])
    
    
        >>> sig_dig(1234243, 3)
        >>> sig_dig(243.3576, 5)
    
            1230.0
            243.36
    
        21
  •  0
  •   Josh Duran    4 年前

    这些答案大多涉及数学、十进制和/或numpy导入或输出字符串值。下面是一个用基本python编写的简单解决方案,它可以处理大数字和小数字,并输出一个浮点值:

    def sig_fig_round(number, digits=3):
        power = "{:e}".format(number).split('e')[1]
        return round(number, -(int(power) - digits))