Convert a decimal.Decimal to a string with a fixed number of characters

Viewed 55

Surprisingly, I can't seem to find this particular question answered previously, although it seems like something that would be pretty standard.

I want to be able to, given a length L, convert any positive Decimal between 1E-100 and 1E+100 (non-inclusive) to a string with exactly L characters. I would like to:

  • Preserve as many digits of precision as possible
  • Avoid adding any extra zeroes (which would misrepresent the precision)
  • Avoid a leading or trailing decimal point (e.g. ".1" or "1.")
  • Only resort to scientific notation if necessary

Here's some examples, where L is 10:

Decimal Value String
0.0000100 0.0000100
0.00001000 0.00001000
0.000010000 1.0000E-05
0.123456789 0.12345679
10 10
1E+01 1E+01
100000000 100000000
100000000.0 100000000
10000000.0 10000000.0
10000000000 1.0000E+10

The most concise way I can think of doing this is:

# x is Decimal, L is length of return string

def posDecimalToString(x, L):
    nDigits = significantDigits(x)
    stdStr = str(x)
    stdLen = len(str(x))

    if (x >= 10 ** L) or (x >= 10 ** nDigits) or (stdLen - L > nDigits - (L - charsUsedByScientific(x))):
        rStr = decimalToScientific(x, L)

    else:
        rStr = stdStr[:10]
        if(rStr[-1:] == "."): rStr = rStr[:-1]
    
    return (" " * (L - len(rStr))) + rStr
        
def decimalToScientific(x, L):
    if significantDigits(x) > (L - charsUsedByScientific(x)):
        return '{:.' + str((L - charsUsedByScientific(x)) - 1) + 'E}'.format(x)
    else:
        return '{:E}'.format(x)

def charsUsedByScientific(x):
    sciStr = '{:E}'.format(x)
    return len(sciStr) - len(sciStr[:sciStr.find('E')])

def significantDigits(x):
    return len(x.as_tuple().digits)

But this just seems far too long-winded for Python. I would think a problem as commonplace as formatting a number with precision would have an implementation in some library. Is there a more concise way to do it?

0 Answers
Related