Python十进制-用于mili(10e-3)和micro(10e-6)的工程符号



下面是困扰我的例子:

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

有没有一种方法可以用工程符号来表示mili(10e-3)和micro(10e-6)?

这里有一个函数可以显式地执行操作,并且还支持为指数使用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)

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

import matplotlib as mpl
formatter = mpl.ticker.EngFormatter()
formatter(10000)
result: '10 k'

原始答案:

基于Julian Smith的出色回答(以及这个回答),我更改了函数以改进以下几点:

  • Python 3兼容(整数除法)
  • 兼容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)

decimal模块遵循十进制算术规范,该规范规定:

这已经过时了-见下文

转换为科学字符串–转换为数字字符串

[…]

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

如果指数小于或等于零,并且调整后的指数大于或等于-6,则该数字将被转换转换为不使用指数表示法的字符形式。

[…]

转换为工程字符串–转换为数字字符串

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

转换完全遵循转换为科学的规则数字字符串,除非是指数型的有限数使用了符号。在这种情况下,通过将小数点定位为前一个、两个或三个字符,转换后的指数将被调整为三的倍数(工程符号)(即,小数点前的部分将在1到999之间)。这可能需要添加一个或两个尾随零。

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

示例:

对于左边的每个抽象表示[符号、系数、指数],结果字符串显示在右边。

表示 字符串
[0123,1] "1.23E+3"
[0123,3] "123E+3"
[0123,-10] "12.3E-9"
[1123,-12] &quot-123E-12"
[0,7,-7] "700E-9〃
[0,7,1] "70〃

我意识到这是一个旧线程,但它确实接近搜索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

«full»引号显示错误

decimal模块确实遵循专有的(IBM)十进制算术规范。完整引用此IBM规范清楚地显示了decimal.to_eng_string()的问题(重点添加):

转换为工程字符串–转换为数字字符串

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

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

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

IBM的这一专有规范实际上承认,对于具有无限十进制表示的数字,不使用工程符号,而是使用普通的科学符号!这显然是打开Python错误报告的错误行为。

解决方案

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

与上面的答案一样,但更紧凑一点:

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'

我编写了一个名为sciform的包,以涵盖这个和stdlib中没有很好涵盖的其他格式化用例。支持工程符号(尾数m具有1<=m<1000)和移位工程符号(尾数m具有0.1<=m<100)。

CCD_ 10具有可用于格式化CCD_ 11对象的自定义格式规范迷你语言。这里r标志工程符号,#替代标志标志移位工程符号。

print(f'{SciNum(0.0001):r}')
# '100e-06'
print(f'{SciNum(0.0001):#r}')
# '0.1e-03'

也可以使用FormatOptionsFormatter对象以面向对象的方式进行格式化。

from sciform import FormatOptions as Fo
from sciform import Formatter, ExpMode
sform = Formatter(Fo(exp_mode=ExpMode.ENGINEERING))
print(sform(0.0001))
# 100e-06

最新更新