I would like to keep the total number of digits (before and after the decimal point) of a float constant in python.
For example, if I want to impose a fixed width of 7:
1234.567890123
would become
1234.567
but
12345.678901234
would become
12345.67
Fixing the number of decimals does not work in this case since it depends on how many digits I have before the decimal point. I also tried the [width] option but it impose a minimum width and I need a maximum.
Thanks for your input!
Just by using your example,
a = 1234.567890123
b = 12345.678901234
str(a)[:8] # gives '1234.567'
str(b)[:8] # gives '12345.67'
The simplest solution is probably to use the exponential format with one less than the number of digits.
"{0:.6e}".format(1234.457890123) = '1.234568e+03'
I ended up writing this solution that can print floats as well as exponentials but it's probably unnecessarily long for most needs.
import numpy as np
def sigprint(number,nsig):
"""
Returns a string with the given number of significant digits.
For numbers >= 1e5, and less than 0.001, it does exponential notation
This is almost what ":.3g".format(x) does, but in the case
of '{:.3g}'.format(2189), we want 2190 not 2.19e3. Also in the case of
'{:.3g}'.format(1), we want 1.00, not 1
"""
if ((abs(number) >= 1e-3) and (abs(number) < 1e5)) or number ==0:
place = decplace(number) - nsig + 1
decval = 10**place
outnum = np.round(np.float(number) / decval) * decval
## Need to get the place again in case say 0.97 was rounded up to 1.0
finalplace = decplace(outnum) - nsig + 1
if finalplace >= 0: finalplace=0
fmt='.'+str(int(abs(finalplace)))+'f'
else:
stringnsig = str(int(nsig-1))
fmt = '.'+stringnsig+'e'
outnum=number
wholefmt = "{0:"+fmt+"}"
return wholefmt.format(outnum)
def decplace(number):
"""
Finds the decimal place of the leading digit of a number. For 0, it assumes
a value of 0 (the one's digit)
"""
if number == 0:
place = 0
else:
place = np.floor(np.log10(np.abs(number)))
return place
You can set the precision when using decimal
It sounds like you also want to round down, but could choose other rounding options if you like. You create a context that includes the precision, rounding logic, and a few other options. You can apply the context to all future operations with setcontext
, a single number using normalize
, or with a context manager using localcontext
.
import decimal
ctx = decimal.Context(prec=7, rounding=decimal.ROUND_DOWN)
print(decimal.Decimal.from_float(1234.567890123).normalize(ctx))
print(decimal.Decimal.from_float(12345.678901234).normalize(ctx))