我正在尝试使用Python以工程格式打印数字,但似乎无法使其工作。语法看起来很简单,但它就是无法工作。
>>> import decimal
>>> x = decimal.Decimal(1000000)
>>> print x
1000000
>>>> print x.to_eng_string()
1000000
我弄不明白为什么会这样。这两个值不相等(一个是字符串,另一个是整数)。在 decimal
中设置各种上下文似乎也没有帮助。有什么线索或想法吗?
我正在尝试使用Python以工程格式打印数字,但似乎无法使其工作。语法看起来很简单,但它就是无法工作。
>>> import decimal
>>> x = decimal.Decimal(1000000)
>>> print x
1000000
>>>> print x.to_eng_string()
1000000
我弄不明白为什么会这样。这两个值不相等(一个是字符串,另一个是整数)。在 decimal
中设置各种上下文似乎也没有帮助。有什么线索或想法吗?
要使其工作,您必须首先将小数标准化:
>>> x = decimal.Decimal ('10000000')
>>> x.normalize()
Decimal('1E+7')
>>> x.normalize().to_eng_string()
'10E+6'
这样的原因可以通过深入源代码来发现。
如果您查看 Python 2.7.3 源代码树中的 to_eng_string()
(Lib/decimal.py
,从此处下载gzipped源tar ball),它只是使用 eng
设置为true 调用了 __str__
。
然后您可以看到,它决定了小数点左侧有多少位数字:
leftdigits = self._exp + len(self._int)
下表显示这两个变量的值:
._exp ._int len leftdigits
----- --------- --- ----------
Decimal (1000000) 0 '1000000' 7 7
Decimal ('1E+6') 6 '1' 1 7
接下来的代码如下:
if self._exp <= 0 and leftdigits > -6:
# no exponent required
dotplace = leftdigits
elif not eng:
# usual scientific notation: 1 digit on left of the point
dotplace = 1
elif self._int == '0':
# engineering notation, zero
dotplace = (leftdigits + 1) % 3 - 1
else:
# engineering notation, nonzero
dotplace = (leftdigits - 1) % 3 + 1
你可以看到,除非在特定范围内已经有一个指数(self._exp > 0 or leftdigits <= -6
),否则在字符串表示中不会给它加上指数。
进一步的调查显示了这种行为的原因。查看代码本身,你会发现它基于General Decimal Arithmetic Specification
(PDF文档请点击这里)。
如果你在该文档中搜索to-scientific-string
(to-engineering-string
就是基于它),它的某些部分(由我改写,并加粗)如下:
“to-scientific-string”操作将数字转换为字符串,如果需要指数,则使用科学计数法。此操作不受上下文影响。
如果数字是有限数字,则:
首先,系数使用数字0到9转换为十进制字符串,没有前导零(除非其值为零,在这种情况下使用单个0字符)。
接下来,计算调整后的指数;这是指数加上转换系数的长度减1。也就是说,exponent+(clength-1),其中clength是系数的十进制数字长度。
如果指数小于或等于零且调整后的指数大于或等于-6,则该数字将被转换为不使用指数表示法的字符形式。在这种情况下,如果指数为零,则不添加小数点。否则(指数将为负),小数点将插入到小数点右侧的字符数中。必要时在已转换的系数左侧添加“0”字符。如果在插入此字符之后小数点之前没有任何字符,则会加上传统的“0”字符前缀。
换句话说,它所做的正是标准告诉它要做的事情。
我知道这是一个旧的帖子,但它确实接近搜索“python工程符号”的顶部。
我是一名工程师,喜欢“工程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
这个软件包还支持比较和其他简单的数值操作。
可以使用内置的printf格式化字符串来解决。
假设(向前)您现在使用的是内置的 Python 3,因为所有格式化相关的事情都得到了极大的简化和规范化(我建议将此速查表添加到书签中)。
In [1]: myfloat = 0.000000001
In [2]: print(f'{myfloat:g}')
1e-09
在 Python 2 中仍然可行的旧语法为:
In [17]: '%g' % myfloat
Out[17]: '1e-09'
另一种漂亮的格式如下:
In [3]: print(f'{myfloat:E}')
1.000000E-09
你可以在其中对输出进行一些控制:
In [4]: print(f'{myfloat:.3E}')
1.000E-09
我觉得你需要自己动手:
from math import log10
def eng_str(x):
y = abs(x)
exponent = int(log10(y))
engr_exponent = exponent - exponent%3
z = y/10**engr_exponent
sign = '-' if x < 0 else ''
return sign+str(z)+'e'+str(engr_exponent)
尽管您可能希望在z
部分的格式上更加小心......
尚未经过充分测试,如果发现错误请随时编辑
matplotlib也可以使用:
>>> from matplotlib.ticker import EngFormatter
>>> engFormat = EngFormatter()
>>> print(engFormat(1000))
1 k
我让你参考https://matplotlib.org/stable/api/ticker_api.html获取更多详细信息和选项。 另一个更复杂的例子:
>>> from matplotlib.ticker import EngFormatter
>>> engFormat = EngFormatter(unit='Hz',places=2,sep='')
>>> print(engFormat(1000))
1.00kHz
QuantiPhy 是另一个可以帮助您的软件包。 QuantiPhy 提供了 Quantity 类,用于将值和单位组合成一个对象。它可以以多种方式格式化数量。通常人们使用 SI 比例因子,但也支持工程形式。安装方法:
pip3 install quantiphy
>>> from quantiphy import Quantity
>>> q = Quantity(10000000)
>>> q.render(form='eng')
'10e6'
>>> print(q)
10M
>>> Quantity.set_prefs(form='eng')
>>> print(q)
10e6
>>> f = Quantity(10_000_000, 'Hz')
>>> print(f)
10e6 Hz
Quantity是float的子类,因此您可以在任何需要使用float的地方使用Quantity。
QuantiPhy是一个功能强大的包,得到了良好的支持和文档。我鼓励您尝试一下。
我的解决方案(我也遇到了同样的问题) 这是一个浅显的想法,没有花哨的编程, 快速而简单,但对我来说还可以 :). 它包含了一个快速测试。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
converts float number to string in engineering form
xxx.xxx [G M k _ m u n p]
"""
def F(f):
if f < 0.0:
sgn = "-"# = True
f = -f
else:
sgn = "+"
if f > 999999999999.999999999999:
return sgn+"max.---"+">" # "overflow error"
elif f > 999999999.999999999999:
v = f/1000000000, "G"
elif f > 999999.999999999999:
v = f/1000000.0, "M"
elif f > 999.999999999999:
v = f/1000.0, "k"
elif f > 0.999999999999:
v = f , " "
elif f > 0.000999999999:
v = f*1000 , "m"
elif f > 0.000000999999:
v = f*1000000.0 , "u"
elif f > 0.000000000999:
v = f*1000000000.0 , "n"
elif f > 0.000000000000999:
v = f*1000000000000.0 , "p"
elif f > 0.000000000000000999:
v = f*1000000000000.0 , "p"
else:
return sgn+"min.---"+"<" # "underflow error"
n = v[0]
if sgn == "-":
n = - n
# to sIII.FFF s = sign, I = integer digit, F = fractional digit
n = "{:+08.3f}".format(n)
return n + v[1]
if __name__ == "__main__":
# test
s = ""
f = 0.0000000000000001111111111
for a in range(55):
f = f * 3.333
v = F(f)
print("{:03d} {:-37.18f}".format(a,f),v)
v = F(-f)
print("{:03d} {:-37.18f}".format(a,-f),v)
类似于 Decimal(s).normalize().to_eng_string()
,但后者不使用科学计数法表示 e-3 和 e-6。
from decimal import Decimal
def to_eng(v: float | str | Decimal) -> str:
"""
Convert a floating point value to engineering notation.
"""
dec = Decimal(v).normalize().as_tuple()
sign = '-' if dec.sign else ''
digits = ''.join(str(digit) for digit in dec.digits)
exp = dec.exponent
# number of digits to left of decimal point
leftdigits = exp + len(digits)
dotplace = (leftdigits - 1) % 3 + 1
if dotplace <= 0:
intpart = '0'
fracpart = '.' + '0' * -dotplace + digits
elif dotplace >= len(digits):
intpart = digits + '0' * (dotplace - len(digits))
fracpart = ''
else:
intpart = digits[:dotplace]
fracpart = '.' + digits[dotplace:]
if leftdigits == dotplace:
exps = ''
else:
exps = f'e{leftdigits - dotplace}'
return sign + intpart + fracpart + exps
现在是2020年!全球爆发了COVID-19疫情。你需要使用f-string字面值来进行这种工作:
在Python3.6.4及以上版本中,您可以这样做
a = 3e-4
print(f"{a:.0E}")