【问题标题】:Python Decimal - engineering notation for mili (10e-3) and micro (10e-6)Python Decimal - mili (10e-3) 和 micro (10e-6) 的工程符号
【发布时间】:2013-07-31 14:32:23
【问题描述】:

这是困扰我的例子:

>>> x = decimal.Decimal('0.0001')
>>> print x.normalize()
>>> print x.normalize().to_eng_string()
0.0001
0.0001

有没有办法用工程符号来表示 mili (10e-3) 和 micro (10e-6)?

【问题讨论】:

  • 这是您要找的吗? stackoverflow.com/questions/6913532/…
  • 不。工程符号是浮点表示,其中指数仅为 3 的倍数,尾数永远不会超过 3 位。 Reference
  • 那么,这个工程符号是100E-6
  • @sihrc 是的,没错。
  • 看起来零有你的答案。如果异常困扰您,您可能可以实现自己的代码来处理异常。

标签: python python-2.7 decimal


【解决方案1】:

这是一个明确执行操作的函数,并且还支持对指数使用 SI 后缀:

def eng_string( x, format='%s', si=False):
    '''
    Returns float/int value <x> formatted in a simplified engineering format -
    using an exponent that is a multiple of 3.

    format: printf-style string used to format the value before the exponent.

    si: if true, use SI suffix for exponent, e.g. k instead of e3, n instead of
    e-9 etc.

    E.g. with format='%.2f':
        1.23e-08 => 12.30e-9
             123 => 123.00
          1230.0 => 1.23e3
      -1230000.0 => -1.23e6

    and with si=True:
          1230.0 => 1.23k
      -1230000.0 => -1.23M
    '''
    sign = ''
    if x < 0:
        x = -x
        sign = '-'
    exp = int( math.floor( math.log10( x)))
    exp3 = exp - ( exp % 3)
    x3 = x / ( 10 ** exp3)

    if si and exp3 >= -24 and exp3 <= 24 and exp3 != 0:
        exp3_text = 'yzafpnum kMGTPEZY'[ ( exp3 - (-24)) / 3]
    elif exp3 == 0:
        exp3_text = ''
    else:
        exp3_text = 'e%s' % exp3

    return ( '%s'+format+'%s') % ( sign, x3, exp3_text)

【讨论】:

  • 这是一个很棒的功能!它运作良好。我建议进行一项改进,例如 x = numpy.float64(x) 因为它不能完全处理整数
  • 这很好,但正如保罗建议的那样,它不适用于整数。建议添加import mathx=float(x) (不需要带numpy进去..?)
  • 另外两个建议:对于python3,我们需要显式地进行整数除法,所以'yzafpnum kMGTPEZY'[ exp3 // 3 + 8] 对于x=0,我们需要一个case,比如if x==0: exp=0, exp3=0
【解决方案2】:

编辑: Matplotlib 实现了工程格式化程序,因此一种选择是直接使用 Matplotlibs 格式化程序,例如:

import matplotlib as mpl
formatter = mpl.ticker.EngFormatter()
formatter(10000)

result: '10 k'

原答案:

基于 Julian Smith 的出色回答(和this 回答),我更改了功能以改进以下几点:

  • Python3 兼容(整数除法)
  • 兼容0输入
  • 四舍五入到有效位数,默认为 3,不打印尾随零

所以这里是更新的函数:

import math
def eng_string( x, sig_figs=3, si=True):
    """
    Returns float/int value <x> formatted in a simplified engineering format -
    using an exponent that is a multiple of 3.

    sig_figs: number of significant figures

    si: if true, use SI suffix for exponent, e.g. k instead of e3, n instead of
    e-9 etc.
    """
    x = float(x)
    sign = ''
    if x < 0:
        x = -x
        sign = '-'
    if x == 0:
        exp = 0
        exp3 = 0
        x3 = 0
    else:
        exp = int(math.floor(math.log10( x )))
        exp3 = exp - ( exp % 3)
        x3 = x / ( 10 ** exp3)
        x3 = round( x3, -int( math.floor(math.log10( x3 )) - (sig_figs-1)) )
        if x3 == int(x3): # prevent from displaying .0
            x3 = int(x3)

    if si and exp3 >= -24 and exp3 <= 24 and exp3 != 0:
        exp3_text = 'yzafpnum kMGTPEZY'[ exp3 // 3 + 8]
    elif exp3 == 0:
        exp3_text = ''
    else:
        exp3_text = 'e%s' % exp3

    return ( '%s%s%s') % ( sign, x3, exp3_text)

【讨论】:

  • 你为什么要导入 FuncFormatter 而不是使用它?但是你正在使用 np (numpy?)
  • 你说得对,导入是不必要的。我更新了答案,谢谢!
  • 这个答案对于负数是不正确的。看起来第一个 elif 应该是 if 否则相应的 else 块中的所有计算都将被跳过。
  • 这里 numpy 的使用是可选的。该函数适用于 Python 标准库数学模块中的等效函数。
  • 我同意你们的两个 cmets。此函数将始终将标量作为输入,因此数学模块似乎是更好的选择。我更新了答案,谢谢。
【解决方案3】:

decimal 模块遵循Decimal Arithmetic Specification,它声明:

to-scientific-string – 转换为数字字符串

[...]

首先将系数转换为以十为底的字符串,使用字符 0 到 9,不带前导零(除非它的值为零,在这种情况下使用单个 0 字符)。 接下来,计算调整后的指数;这是指数加上转换系数中的字符数减去一。也就是说,exponent+(clength-1),其中 clength 是十进制数字的系数长度。

如果指数小于或等于0,并且调整后的指数大于或等于-6,数字将被转换 转换为不使用指数符号的字符形式。

[...]

to-engineering-string – 转换为数字字符串

此操作将数字转换为字符串,使用工程 如果需要指数,则表示法。

转换完全遵循科学转换规则 数字字符串 除非是指数的有限数 使用符号。

或者,换句话说:

>>> for n in (10 ** e for e in range(-1, -8, -1)):
...     d = Decimal(str(n))
...     print d.to_eng_string()
... 
0.1
0.01
0.001
0.0001
0.00001
0.000001
100E-9

【讨论】:

  • 我正在寻找解决方法。这样to_eng_string() 适用于较小的数字。在标准方式中,milimicro 前缀被完全忽略,而且它们经常被忽略。
  • @Alan 这是一个稍微不同的问题——你要求“所有情况的工程符号”,你得到了,每个规范。您所追求的是“我的工程符号变体”,这是可以理解的标准库中缺少的。
  • 我知道混乱来自哪里。 “所有情况”是指它不会歧视milimicro。将更改主题以使其更清楚。
  • @ZeroPiraeus 这个专有 (IBM) 规范实际上承认它没有对无限数应用工程符号!作为一名工程师,我同意 Alan 的观点,即 Python 在采用这个专有规范时选择不当。
  • @Alan 你说对了,engineering notation 在实现这个专有规范时并不适用于所有情况。当完整引用本规范时,这一点变得显而易见。另请参阅我的其他评论。
【解决方案4】:

我意识到这是一个旧线程,但它确实接近搜索 python engineering notation 的顶部,并且将这些信息放在此处似乎是谨慎的。

我是一名喜欢“工程101”工程单元的工程师。我什至不喜欢0.1uF 之类的名称,我希望它读作100nF。我使用了 Decimal 类,并不喜欢它在可能值范围内的行为,所以我推出了一个名为 engineering_notation 的包,它可以通过 pip 安装。

pip install engineering_notation

从 Python 内部:

>>> from engineering_notation import EngNumber
>>> EngNumber('1000000')
1M
>>> EngNumber(1000000)
1M
>>> EngNumber(1000000.0)
1M
>>> EngNumber('0.1u')
100n
>>> EngNumber('1000m')
1

这个包还支持比较和其他简单的数值运算。

https://github.com/slightlynybbled/engineering_notation

【讨论】:

    【解决方案5】:

    «完整»引述说明了问题所在!

    decimal 模块确实遵循 专有 (IBM) Decimal Arithmetic Specification. 引用此 IBM 规范全文清楚地表明 decimal.to_eng_string() 有什么问题(强调添加):

    to-engineering-string – 转换为数字字符串

    此操作将数字转换为字符串,使用工程 如果需要指数,则表示法。

    转换完全遵循科学转换规则 数字字符串 除外在有限数的情况下,其中指数 使用符号。在这种情况下,通过在小数点前面放置一个、两个或三个字符(即小数点前的部分),将转换后的指数调整为三的倍数(工程符号)点的范围为 1 到 999)。这可能需要添加一个或两个尾随零。

    如果调整后小数点后面没有数字,则不添加。如果最终指数为零,则没有指示字母和指数后缀。

    这个专有的 IBM 规范实际上承认不对带有 infinite decimal representation 的数字应用工程符号,而是使用普通的科学记数法!这显然是打开了Python bug report 的错误行为。

    解决方案

    from math import floor, log10
    
    def powerise10(x):
        """ Returns x as a*10**b with 0 <= a < 10
        """
        if x == 0: return 0,0
        Neg = x < 0
        if Neg: x = -x
        a = 1.0 * x / 10**(floor(log10(x)))
        b = int(floor(log10(x)))
        if Neg: a = -a
        return a,b
    
    def eng(x):
        """Return a string representing x in an engineer friendly notation"""
        a,b = powerise10(x)
        if -3 < b < 3: return "%.4g" % x
        a = a * 10**(b % 3)
        b = b - b % 3
        return "%.4gE%s" % (a,b)
    

    来源:https://code.activestate.com/recipes/578238-engineering-notation/

    测试结果

    >>> eng(0.0001)
    100E-6
    

    【讨论】:

    • 你的意思不是真的是“无限的数字”,对吗?你建议Decimal('inf').to_eng_string() 的输出应该是什么?
    • 顺便说一句,引用的句子很危险地接近模棱两可。它应该读作“除了(那些使用指数符号的有限数的情况)”,而不是“除了使用指数符号的有限数”。
    • @MarkDickinson 我同意,引用的规范模棱两可的。我编辑了我的文字,清楚地表明它的意思是infinite decimal representation
    • 我认为您误解了规范中的那句话。它与无限数无关,也与没有有限长度十进制表示的数字无关。 (decimal 模块的重点是 所有 可表示的数字都有一个有限长度的十进制表示。)这只是说 finite的工程格式和科学格式是相同的> 数量不够大或小到需要指数的数字。无限不会出现在任何地方。
    【解决方案6】:

    类似于上面的答案,但更紧凑:

    from math import log10, floor
    
    def eng_format(x,precision=3):
        """Returns string in engineering format, i.e. 100.1e-3"""
        x = float(x)  # inplace copy
        if x == 0:
            a,b = 0,0
        else: 
            sgn = 1.0 if x > 0 else -1.0
            x = abs(x) 
            a = sgn * x / 10**(floor(log10(x)))
            b = int(floor(log10(x)))
    
        if -3 < b < 3: 
            return ("%." + str(precision) + "g") % x
        else:
            a = a * 10**(b % 3)
            b = b - b % 3
            return ("%." + str(precision) + "gE%s") % (a,b)
    

    试用:

    In [10]: eng_format(-1.2345e-4,precision=5)
    Out[10]: '-123.45E-6'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-08
      • 1970-01-01
      • 2017-05-16
      • 1970-01-01
      • 2016-06-26
      • 2017-09-26
      • 2016-09-15
      相关资源
      最近更新 更多