Python Decimal - engineering notation for mili (10e-3) and micro (10e-6)

前端 未结 5 1317
情话喂你
情话喂你 2021-01-01 23:53

Here is the example which is bothering me:

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


        
相关标签:
5条回答
  • 2021-01-02 00:08

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

    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)
    
    0 讨论(0)
  • 2021-01-02 00:08

    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
    
    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)
    

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

    Test result

    >>> eng(0.0001)
    100E-6
    
    0 讨论(0)
  • 2021-01-02 00:13

    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

    0 讨论(0)
  • 2021-01-02 00:21

    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 in range(-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
    
    0 讨论(0)
  • 2021-01-02 00:31

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

    import matplotlib as mpl
    formatter = 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
    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)
    
    0 讨论(0)
提交回复
热议问题