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

Pet*_*ham 130 python math rounding

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

1234 - > 1000

0.12 - > 0.1

0.012 - > 0.01

0.062 - > 0.06

6253 - > 6000

1999年 - > 2000年

有没有一种很好的方法可以使用Python库来实现这一点,还是我必须自己编写?

Evg*_*eny 134

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

>>> round(1234, -3)
1000.0
Run Code Online (Sandbox Code Playgroud)

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

如果它大于1,你可能需要注意将float转换为整数.

  • round_to_n = lambda x,n:round(x,-int(floor(log10(x)))+(n - 1)) (61认同)
  • 你应该使用`log10(abs(x))`,否则负数将失败(当然,分别处理`x == 0`) (21认同)
  • `round_to_n = lambda x, n: x if x == 0 else round(x, -int(math.floor(math.log10(abs(x)))) + (n - 1))` 防止 `x= =0`和`x<0`谢谢@RoyHyunjinHan和@TobiasKienzler。无法防止像 math.inf 这样的未定义,或者像 None 等垃圾 (5认同)
  • 这是正确的解决方案。使用 `log10` 是确定如何舍入它的唯一正确方法。 (3认同)
  • 我已经创建了一个现在可以执行此操作的程序包,并且可能比这个更容易,更强大.[帖子链接](/sf/answers/3089396481/),[回购链接](https://github.com/BebeSparkelSparkel/to-precision).希望这可以帮助! (2认同)

Pet*_*ham 95

字符串格式化中的%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'
Run Code Online (Sandbox Code Playgroud)

  • 请注意,%g的行为并不总是正确的.特别是它*总是*修剪尾随零,即使它们很重要.数字1.23400有6位有效数字,但"%.6g"%(1.23400)将导致"1.234"不正确.此博客文章中的更多详细信息:http://randlet.com/blog/python-significant-figures-format/ (10认同)
  • OP的要求是将1999年格式化为'2000',而不是'2000.0'.我无法通过一种微不足道的方式来改变你的方法来实现这一目标. (7认同)
  • 就像 Evgeny 的答案中的方法一样,这无法正确地将“0.075”四舍五入为“0.08”。它返回`0.07`。 (3认同)
  • `round_sig = lambda f,p: float(('%.' + str(p) + 'e') % f)` 允许您调整有效位数! (2认同)

小智 46

如果您想要除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
Run Code Online (Sandbox Code Playgroud)

  • round_sig(-0.0232) - > math domain error,你可能想在那里添加一个abs();) (8认同)
  • 就像 Evgeny 和 Peter Graham 的答案中的方法一样,这无法正确地将“0.075”四舍五入到“0.08”。它返回`0.07`。 (2认同)
  • 对于round_sig(0),它也失败了. (2认同)
  • @Gabriel这是在计算机上运行的python的内置“功能”,并通过函数round的行为体现出来。https://docs.python.org/2/tutorial/floatingpoint.html#tut-fp-issues (2认同)
  • @Gabriel我添加了一个答案,解释了为什么你**应该**期望从四舍五入“0.075”中得到0.7!请参阅/sf/answers/3988242541/ (2认同)
  • Python 3 注意:int() 可以省略,因为 math.floor 返回整数。 (2认同)

小智 17

print('{:g}'.format(float('{:.1g}'.format(12.345))))
Run Code Online (Sandbox Code Playgroud)

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

  1. 完全解决了OP问题
  2. 它并没有需要任何额外的包
  3. 需要任何用户定义的辅助功能数学运算

对于任意数量n的有效数字,您可以使用:

print('{:g}'.format(float('{:.{p}g}'.format(i, p=n))))
Run Code Online (Sandbox Code Playgroud)

测试:

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']
Run Code Online (Sandbox Code Playgroud)

注意:使用此解决方案,无法从输入动态调整有效数字的数量,因为没有标准方法可以区分具有不同数量的尾随零(3.14 == 3.1400)的数字.如果需要这样做,则需要非标准函数,如精度软件包中提供的函数.

  • 哇,你的答案需要真正从上到下阅读;)这个双重转换技巧很肮脏,但很简洁。(请注意,1999 格式为“2000.0”*建议* 5 个有效数字,因此必须再次经过“{:g}”。)一般来说,尾随零的整数在有效数字方面是不明确的,除非采用某种技术(就像上面最后一个重要的上划线一样)被使用。 (3认同)
  • @TomSwirly 你获得最大精度 6 的原因是第一个“{:g}”(基于文档:“如果没有给出精度,则使用 6 位有效数字的精度”)。如果您也传递了“{:g}”中的精度,您将得到正确的结果。代码为: `'{:.{p}g}'.format(float('{:.{p}g}'.format(0.123456789012, p=12)),p=12)` 导致 ` 0.123456789012` (3认同)
  • 由于某种原因,该解决方案无法满足更高的精度。`'{:g}'.format(float('{:.{p}g}'.format(0.123456789012, p=20)))` 结果为 `0.123457`。 (2认同)

Sam*_*son 10

为了直接回答这个问题,这是我使用R 函数命名的版本:

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)
Run Code Online (Sandbox Code Playgroud)

我发布此答案的主要原因是评论抱怨“0.075”四舍五入为 0.07 而不是 0.08。正如“C 新手”所指出的那样,这是由于具有有限精度和基数 2 表示的浮点运算的组合。实际可以表示的最接近 0.075 的数字略小,因此四舍五入的结果与您可能天真地预期的不同。

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

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

0.075.hex()
Run Code Online (Sandbox Code Playgroud)

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

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

0x13333333333333 / 16**13 * 2**-4
Run Code Online (Sandbox Code Playgroud)

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

现在我们对如何表示浮点数有了一些了解,我们可以使用该decimal模块为我们提供更高的精度,向我们展示正在发生的事情:

from decimal import Decimal

Decimal(0x13333333333333) / 16**13 / 2**4
Run Code Online (Sandbox Code Playgroud)

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

  • 这很好地解释了为什么 *在代码级别* 0.075 向下舍入为 0.07,但我们(在物理科学领域)被教导要始终向上舍入而不是向下舍入。因此,尽管存在浮点精度问题,预期行为实际上是 0.08。 (3认同)
  • 我并不困惑。当我输入 0.075 时,我实际上输入的是 0.075。无论代码中的浮点数学发生什么,我都不关心。 (2认同)

Wil*_*ack 7

我已经创建了包含精确度的包,可以满足您的需求.它允许您给出数字或多或少的重要数字.

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

在接受的答案中有一条线

>>> round_to_1(1234243)
1000000.0
Run Code Online (Sandbox Code Playgroud)

这实际上指定了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'
Run Code Online (Sandbox Code Playgroud)

它还将围绕最后一个有效数字,并且如果未指定表示法,则可以自动选择要使用的符号:

>>> to_precision(599, 2)
'600'
>>> to_precision(1164, 2)
'1.2e3'
Run Code Online (Sandbox Code Playgroud)

  • 它有一个错误: std_notation(9.999999999999999e-05, 3) 给出: '0.00010' 这只有 2 个有效数字 (2认同)

Aut*_*umn 6

发布的答案是给出的最佳答案,但它有许多限制,并且不会产生技术上正确的有效数字。

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.
Run Code Online (Sandbox Code Playgroud)


Cri*_*low 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)
Run Code Online (Sandbox Code Playgroud)


AJP*_*AJP 5

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
Run Code Online (Sandbox Code Playgroud)

希望能采纳上面所有答案中最好的答案(减去能够将其作为单行 lambda ;))。尚未探索,请随意编辑此答案:

round_to_n(1e15 + 1, 11)  # 999999999999999.9
Run Code Online (Sandbox Code Playgroud)