Skip to content Skip to sidebar Skip to footer

Python Decimal - Engineering Notation For Mili (10e-3) And Micro (10e-6)

Here is the example which is bothering me: >>> x = decimal.Decimal('0.0001') >>> print x.normalize() >>> print x.normalize().to_eng_string() 0.0001 0.000

Solution 1:

Here's a function that does things explicitly, and also has support for using SI suffixes for the exponent:

defeng_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 >= -24and exp3 <= 24and 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)

Solution 2:

EDIT: Matplotlib implemented the engineering formatter, so one option is to directly use Matplotlibs formatter, e.g.:

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

result: '10 k'

Original answer:

Based on Julian Smith's excellent answer (and this answer), I changed the function to improve on the following points:

  • Python3 compatible (integer division)
  • Compatible for 0 input
  • Rounding to significant number of digits, by default 3, no trailing zeros printed

so here's the updated function:

import math
defeng_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 = 0else:
        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 >= -24and exp3 <= 24and 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)

Solution 3:

The decimal module is following the Decimal Arithmetic Specification, which states:

to-scientific-string – conversion to numeric string

[...]

The coefficient is first converted to a string in base ten using the characters 0 through 9 with no leading zeros (except if its value is zero, in which case a single 0 character is used). Next, the adjusted exponent is calculated; this is the exponent, plus the number of characters in the converted coefficient, less one. That is, exponent+(clength-1), where clength is the length of the coefficient in decimal digits.

If the exponent is less than or equal to zero and the adjusted exponent is greater than or equal to -6, the number will be converted to a character form without using exponential notation.

[...]

to-engineering-string – conversion to numeric string

This operation converts a number to a string, using engineering notation if an exponent is needed.

The conversion exactly follows the rules for conversion to scientific numeric string except in the case of finite numbers where exponential notation is used.

Or, in other words:

>>>for n in (10 ** e for e inrange(-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

Solution 4:

I realize that this is an old thread, but it does come near the top of a search for python engineering notation and it seems prudent to have this information located here.

I am an engineer who likes the "engineering 101" engineering units. I don't even like designations such as 0.1uF, I want that to read 100nF. I played with the Decimal class and didn't really like its behavior over the range of possible values, so I rolled a package called engineering_notation that is pip-installable.

pip install engineering_notation

From within Python:

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

This package also supports comparisons and other simple numerical operations.

https://github.com/slightlynybbled/engineering_notation

Solution 5:

The «full» quote shows what is wrong!

The decimal module is indeed following the proprietary (IBM) Decimal Arithmetic Specification. Quoting this IBM specification in its entirety clearly shows what is wrong with decimal.to_eng_string() (emphasis added):

to-engineering-string – conversion to numeric string

This operation converts a number to a string, using engineering notation if an exponent is needed.

The conversion exactly follows the rules for conversion to scientific numeric string except in the case of finite numbers where exponential notation is used. In this case, the converted exponent is adjusted to be a multiple of three (engineering notation) by positioning the decimal point with one, two, or three characters preceding it (that is, the part before the decimal point will range from 1 through 999). This may require the addition of either one or two trailing zeros.

If after the adjustment the decimal point would not be followed by a digit then it is not added. If the final exponent is zero then no indicator letter and exponent is suffixed.

This proprietary IBM specification actually admits to not applying the engineering notation for numbers with an infinite decimal representation, for which ordinary scientific notation is used instead! This is obviously incorrect behaviour for which a Python bug report was opened.

Solution

from math import floor, log10

defpowerise10(x):
    """ Returns x as a*10**b with 0 <= a < 10
    """if x == 0: return0,0
    Neg = x < 0if Neg: x = -x
    a = 1.0 * x / 10**(floor(log10(x)))
    b = int(floor(log10(x)))
    if Neg: a = -a
    return a,b

defeng(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 % 3return"%.4gE%s" % (a,b)

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

Test result

>>>eng(0.0001)
100E-6

Post a Comment for "Python Decimal - Engineering Notation For Mili (10e-3) And Micro (10e-6)"