如何在Python中将数字舍入为有效数字

时间:2010-08-05 00:48:05

标签: python math rounding

我需要对浮点数进行舍入以在UI中显示。例如,一位重要人物:

1234 - > 1000

0.12 - > 0.1

0.012 - > 0.01

0.062 - > 0.06

6253 - > 6000

1999 - > 2000

有没有一种很好的方法可以使用Python库来完成这个,或者我必须自己编写它?

21 个答案:

答案 0 :(得分:130)

您可以使用负数来舍入整数:

>>> 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

如果它大于1,你可能不得不把浮动变成整数。

答案 1 :(得分:87)

字符串格式的

%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'

答案 2 :(得分:42)

如果您想要除1个有效小数以外(或与Evgeny相同):

>>> 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

答案 3 :(得分:9)

print('{:g}'.format(float('{:.1g}'.format(12.345))))

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

  1. 完全解决OP问题
  2. 需要额外包
  3. 需要任何用户定义的辅助功能数学运算
  4. 对于任意数量的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']
    

    注意:使用此解决方案,无法从输入中动态调整有效数字的数量,因为没有标准方法可以区分具有不同数量的尾随零的数字({{1 }})。如果您需要这样做,则需要使用to-precision包中提供的非标准函数。

答案 4 :(得分:5)

我已经创建了可以满足您需求的包to-precision。它允许您给出数字或多或少的重要数字。

它还输出标准,科学和工程符号以及指定数量的有效数字。

在接受的答案中有一行

>>> round_to_1(1234243)
1000000.0

这实际上指定了8个无花果。对于数字1234243,我的图书馆只显示一个重要数字:

>>> 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'

答案 5 :(得分:5)

要将整数舍入为1有效数字,基本思路是将其转换为点数前后1位数的浮点数,然后将其转换回原始整数。

要做到这一点,我们需要知道10的最大功率小于整数。为此我们可以使用log 10功能的楼层。

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)

答案 6 :(得分:4)

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

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)

答案 7 :(得分:3)

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;))。 Haven尚未探索过,请随时编辑此答案:

round_to_n(1e15 + 1, 11)  # 999999999999999.9

答案 8 :(得分:2)

我无法想到任何可以开箱即用的东西。但它对浮点数的处理相当好。

>>> round(1.2322, 2)
1.23

整数比较棘手。它们不是作为基础10存储在内存中,因此重要的地方不是自然而然的事情。一旦它们成为一个字符串,它实现起来相当简单。

或整数:

>>> 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)

答案 9 :(得分:2)

要直接回答这个问题,这是我用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都有相同的问题。

为更详细地显示,我们要求Python将数字格式化为“十六进制”格式:

0.075.hex()

这给我们:0x1.3333333333333p-4。这样做的原因是正常的十进制表示形式通常涉及四舍五入,因此不是计算机实际“看到”数字的方式。如果您不习惯这种格式,则可以使用Python docsC standard这两个有用的参考。

为说明这些数字的工作原理,我们可以通过以下操作回到起点:

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

应打印出0.07516**13是因为小数点后有13个十六进制数字,而2**-4是因为十六进制指数以2为底。

现在我们对浮点数的表示方式有了一些了解,可以使用decimal模块为我们提供更多的精度,向我们展示发生了什么事情:

from decimal import Decimal

Decimal(0x13333333333333) / 16**13 / 2**4

给出:0.07499999999999999722444243844,并希望解释为什么round(0.075, 2)的评估结果为0.07

答案 10 :(得分:1)

如果您想要在不涉及字符串的情况下进行舍入,我发现的链接已隐藏在上述注释中:

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

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

链接上的代码是三行代码:def,doc和return。它有一个错误:你需要检查爆炸对数。这很容易。将输入与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)) ) )

它适用于任何标量数值,如果由于某种原因需要移动响应,则n可以是float。您实际上可以将限制推送到:

sys.float_info.min*sys.float_info.epsilon

如果由于某种原因你正在使用微不足道的价值而不会引发错误。

答案 11 :(得分:1)

发布的答案是给出的最好的答案,但是它有很多局限性,并且不能提供技术上正确的有效数字。

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.

答案 12 :(得分:0)

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

span {
    display:block;
    min-width:9999px; /*remove this*/
    position:absolute;
    top:25px;
    left:0;
}

答案 13 :(得分:0)

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

答案 14 :(得分:0)

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

答案 15 :(得分:0)

给出一个如此详尽的问题,为什么不添加另一个问题

尽管上面的许多内容是可比的,但这更适合我的审美观

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数组,对于负数应该可以正常工作。

我们可能还要增加一个步骤-即使四舍五入为整数,np.round()也会返回一个十进制数(即对于有意义的数字= 2,我们可能会期望返回-460,但相反会得到-460.0)。我们可以添加此步骤以对此进行纠正:

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

不幸的是,最后的步骤不适用于数字数组-亲爱的读者,我会把这个留给你,以弄清楚是否需要。

答案 16 :(得分:0)

这将返回一个字符串,以便结果不包含小数部分,并且正确显示了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))

答案 17 :(得分:0)

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

答案 18 :(得分:0)

如果该数字大于10 **(-decimal_positions),则此函数进行正常的回合,否则增加更多的小数,直到达到有意义的小数位数为止。

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)

希望有帮助。

答案 19 :(得分:0)

使用python 2.6+ new-style formatting(不推荐使用%-style):

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

在python 2.7+中,您可以省略前导0

答案 20 :(得分:0)

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

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))