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?