【问题标题】:Print number in engineering format以工程格式打印编号
【发布时间】:2012-08-31 22:14:23
【问题描述】:

我正在尝试使用 python 将数字打印成工程格式,但我似乎无法让它工作。语法看起来很简单,但它就是行不通。

>>> import decimal 
>>> x = decimal.Decimal(1000000)
>>> print x
1000000
>>>> print x.to_eng_string() 
1000000

我不知道为什么会这样。这两个值不相等(一个是字符串,另一个是 int)。在decimal 中设置各种上下文似乎也无济于事。有什么线索或想法吗?

【问题讨论】:

  • 我在这里找到了解决方案 "%.4g" % x

标签: python python-2.7 decimal


【解决方案1】:

在我看来,您将不得不自己动手:

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 部分的格式方面更加小心...

没有经过很好的测试。如果您发现错误,请随时编辑

【讨论】:

  • 虽然我不介意这个想法,但我更好奇为什么 decimal 类没有做它看起来应该做的事情,或者我是如何错误地使用它
  • @jmurrayufo -- 抱歉,我想我误解了这个问题:)。
【解决方案2】:

要让它工作,你必须先规范化小数:

>>> x = decimal.Decimal ('10000000')

>>> x.normalize()
Decimal('1E+7')

>>> x.normalize().to_eng_string()
'10E+6'

可以通过深入研究源代码来发现其原因。

如果您检查 Python 2.7.3 源代码树中的 to_eng_string()(来自 gzip 压缩源 tar 球 hereLib/decimal.py),它只需调用 __str__ 并将 eng 设置为 true。

然后你可以看到它决定了小数点左边有多少位最初是:

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 &gt; 0 or leftdigits &lt;= -6),否则在字符串表示中不会给出任何指数。


进一步调查显示了这种行为的原因。查看代码本身,您会发现它基于 General Decimal Arithmetic Specification (PDF here)。

如果您在该文档中搜索 to-scientific-stringto-engineering-string 主要基于该文档),它会部分说明(转述,并带有我的粗体字):

“to-scientific-string”操作将数字转换为字符串,使用科学记数法如果需要指数。该操作不受上下文影响。

如果该数是有限数,则:

首先将系数转换为以十为底的字符串,使用字符 0 到 9,不带前导零(除非它的值为零,在这种情况下使用单个 0 字符)。

接下来,计算调整后的指数;这是指数加上转换系数中的字符数减去一。即exponent+(clength-1),其中clength为系数的十进制数字长度。

如果指数小于等于0,且调整后的指数大于等于-6,则不使用指数符号将数字转换为字符形式。在这种情况下,如果指数为零,则不添加小数点。否则(指数将为负数),将插入一个小数点,其中指数的绝对值指定小数点右侧的字符数。必要时将“0”字符添加到转换系数的左侧。如果此插入后小数点前没有字符,则以常规的“0”字符为前缀。

换句话说,它正在做它正在做的事情,因为这是标准告诉它要做的事情。

【讨论】:

  • 为什么要麻烦检查一下?我问了一些具体的事情。然后它决定我不想要那个,只想要我给它的东西? Python 有时让我感到困惑……
  • @jmurrayufo,检查更新。基本上,它这样做是因为标准要求它这样做。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    现在是 2020 年了!并且发生了 COVID-19 大流行。对于这种类型的工作,您必须使用 f 字符串文字:

    用python3.6.4及以上,可以做

    a = 3e-4
    print(f"{a:.0E}")
    

    【讨论】:

    • 请注意,这会返回科学记数法,而不是工程记数法,其中 E 应该是 3 的倍数。问题并不完全清楚这是否可以接受,但在许多用例中很重要.
    • 我在发布我的答案后看到了您的答案,抱歉有重叠。在 CS 中,“工程符号”的定义是在这个答案中给出的 - 也许 OP 造成了混淆,但这个答案不是。
    【解决方案5】:

    QuantiPhy 是另一个可以帮助您解决此问题的软件包。 QuantiPhy 提供了 Quantity 类,用于将值和单位组合到一个对象中。它可以以多种方式格式化数量。通常人们使用 SI 比例因子,但也支持工程形式。安装:

    pip3 install quantiphy
    

    您可以从数字或字符串开始创建 Quantity 对象:

    >>> from quantiphy import Quantity
    >>> q = Quantity(10000000)
    >>> q.render(form='eng')
    '10e6'
    

    QuantiPhy 默认使用 SI 表示法,但您可以将默认形式设置为 'eng':

    >>> print(q)
    10M
    >>> Quantity.set_prefs(form='eng')
    >>> print(q)
    10e6
    

    通常你会指定一个数量的单位:

    >>> f = Quantity(10_000_000, 'Hz')
    >>> print(f)
    10e6 Hz
    

    Quantityfloat 的子类,因此您可以在任何需要使用浮点数的地方使用数量。

    QuantiPhy 是一个功能强大的软件包,它得到了很好的支持和很好的文档记录。我鼓励你试一试。

    【讨论】:

      【解决方案6】:

      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
      

      【讨论】:

        【解决方案7】:

        有一个使用内置printf-style String Formatting的解决方案

        我将假设(及时)您现在使用内置的 python 3,因为所有的格式都被大大简化和规范化了(我建议收藏this cheat sheet

        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
        

        【讨论】:

          猜你喜欢
          • 2010-09-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-29
          相关资源
          最近更新 更多