下面是困扰我的例子:
>>> 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] "-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'
也可以使用
FormatOptions
和Formatter
对象以面向对象的方式进行格式化。from sciform import FormatOptions as Fo from sciform import Formatter, ExpMode sform = Formatter(Fo(exp_mode=ExpMode.ENGINEERING)) print(sform(0.0001)) # 100e-06