Format float with fixed amount of digits python - python

I would like to format my floats with a fixed amount of digits. Right now I'm doing the following
format="%6.6g"
print(format%0.00215165)
print(format%1.23260)
print(format%145.5655)
But this outputs
0.00215165
1.2326
145.565
I also tried format="%6.6f" but it doesn't really give what I want either...
0.002152
1.232600
145.565500
What would be a good way to format the numbers so that all of them have exactly width 6 (and no spaces) like so ?
0.002152
1.232600
145.5655

This is complicated because you want the precision (number of decimals) to depend on the available space, while the general thrust of floating-point formatting is to make the number of significant digits depend on the available space. To do what you want you need a function that computes the desired number of decimals from the log of the number. There isn't, so far as I know, a built-in function that will do this for you.
def decimals(v):
return max(0, min(6,6-int(math.log10(abs(v))))) if v else 6
This simply takes the log of number and truncates it to int. So 10-99 -> 1, 100-999 -> 2 etc. You then use that
result to work out the precision to which the number needs to be formatted. In practice the
function is more complex because of the corner cases: what to do with negative numbers, numbers that underflow, etc.
For simplicity I've deliberately left your figure of 6 decimals hard-coded 3 times in the function.
Then formatting isn't so hard:
>>> v = 0.00215165
>>> "{0:.{1}f}".format(v, decimals(v))
'0.002152'
>>> v2 = 1.23260
>>> "{0:.{1}f}".format(v2, decimals(v2))
'1.232600'
>>> v3 = 145.5655
>>> "{0:.{1}f}".format(v3, decimals(v3))
'145.5655'
>>> vz = 0e0 # behaviour with zero
>>> "{0:.{1}f}".format(vz, decimals(vz))
'0.000000'
>>> vu = 1e-10 # behaviour with underflow
>>> "{0:.{1}f}".format(vu, decimals(vu))
'0.000000'
>>> vo = 1234567 # behaviour when nearly out of space
>>> "{0:.{1}f}".format(vo, decimals(vo))
'1234567'
>>> voo = 12345678 # behaviour when all out of space
>>> "{0:.{1}f}".format(voo, decimals(voo))
'12345678'
You can use %-notation for this instead of a call to format but it is not very obvious or intuitive:
>>> "%.*f" % (decimals(v), v)
'0.002152'
You don't say what you want done with negative numbers. What this approach does is to take an extra
character to display the minus sign. If you don't want that then you need to reduce the number of
decimals for negative numbers.

Related

How can I check the length of a long float? Python is truncating the length [duplicate]

I have some number 0.0000002345E^-60. I want to print the floating point value as it is.
What is the way to do it?
print %f truncates it to 6 digits. Also %n.nf gives fixed numbers. What is the way to print without truncation.
Like this?
>>> print('{:.100f}'.format(0.0000002345E-60))
0.0000000000000000000000000000000000000000000000000000000000000000002344999999999999860343602938602754
As you might notice from the output, it’s not really that clear how you want to do it. Due to the float representation you lose precision and can’t really represent the number precisely. As such it’s not really clear where you want the number to stop displaying.
Also note that the exponential representation is often used to more explicitly show the number of significant digits the number has.
You could also use decimal to not lose the precision due to binary float truncation:
>>> from decimal import Decimal
>>> d = Decimal('0.0000002345E-60')
>>> p = abs(d.as_tuple().exponent)
>>> print(('{:.%df}' % p).format(d))
0.0000000000000000000000000000000000000000000000000000000000000000002345
You can use decimal.Decimal:
>>> from decimal import Decimal
>>> str(Decimal(0.0000002345e-60))
'2.344999999999999860343602938602754401109865640550232148836753621775217856801120686600683401464097113374472942165409862789978024748827516129306833728589548440037314681709534891496105046826414763927459716796875E-67'
This is the actual value of float created by literal 0.0000002345e-60. Its value is a number representable as python float which is closest to actual 0.0000002345 * 10**-60.
float should be generally used for approximate calculations. If you want accurate results you should use something else, like mentioned Decimal.
If I understand, you want to print a float?
The problem is, you cannot print a float.
You can only print a string representation of a float. So, in short, you cannot print a float, that is your answer.
If you accept that you need to print a string representation of a float, and your question is how specify your preferred format for the string representations of your floats, then judging by the comments you have been very unclear in your question.
If you would like to print the string representations of your floats in exponent notation, then the format specification language allows this:
{:g} or {:G}, depending whether or not you want the E in the output to be capitalized). This gets around the default precision for e and E types, which leads to unwanted trailing 0s in the part before the exponent symbol.
Assuming your value is my_float, "{:G}".format(my_float) would print the output the way that the Python interpreter prints it. You could probably just print the number without any formatting and get the same exact result.
If your goal is to print the string representation of the float with its current precision, in non-exponentiated form, User poke describes a good way to do this by casting the float to a Decimal object.
If, for some reason, you do not want to do this, you can do something like is mentioned in this answer. However, you should set 'max_digits' to sys.float_info.max_10_exp, instead of 14 used in the answer. This requires you to import sys at some point prior in the code.
A full example of this would be:
import math
import sys
def precision_and_scale(x):
max_digits = sys.float_info.max_10_exp
int_part = int(abs(x))
magnitude = 1 if int_part == 0 else int(math.log10(int_part)) + 1
if magnitude >= max_digits:
return (magnitude, 0)
frac_part = abs(x) - int_part
multiplier = 10 ** (max_digits - magnitude)
frac_digits = multiplier + int(multiplier * frac_part + 0.5)
while frac_digits % 10 == 0:
frac_digits /= 10
scale = int(math.log10(frac_digits))
return (magnitude + scale, scale)
f = 0.0000002345E^-60
p, s = precision_and_scale(f)
print "{:.{p}f}".format(f, p=p)
But I think the method involving casting to Decimal is probably better, overall.

Append a plus sign before positive numbers

I want to append a plus sign before numbers. I am already using format specifier:
"{0:+03f}".format(x)
And I've heard about those two as well, but I don't know how to use them:
"%+d" or "%+f"
My problem with the first one is the fact that the number after format is in float type.
For example, I am making a small program to calculate quadratic function and I am not satisfied with output like this:
f(x) = 2x^2+2.000x-4.000000
Those zeros are making it looks weird.
If not above, is there any solution to get rid of zeros when there is nothing, but only zeros after the dot?
Perhaps %g is what you're looking for?
>>> '%+g' % 2.
'+2'
>>> '%+g' % 2.1
'+2.1'
>>> '%+g' % 2.10001
'+2.10001'
The exact definition of %g is as follows:
General format. For a given precision p >= 1, this rounds the number
to p significant digits and then formats the result in either
fixed-point format or in scientific notation, depending on its
magnitude.
The precise rules are as follows: suppose that the result formatted
with presentation type 'e' and precision p-1 would have exponent exp.
Then if -4 <= exp < p, the number is formatted with presentation type
'f' and precision p-1-exp. Otherwise, the number is formatted with
presentation type 'e' and precision p-1. In both cases insignificant
trailing zeros are removed from the significand, and the decimal point
is also removed if there are no remaining digits following it.
Positive and negative infinity, positive and negative zero, and nans,
are formatted as inf, -inf, 0, -0 and nan respectively, regardless of
the precision.
A precision of 0 is treated as equivalent to a precision of 1. The
default precision is 6.
(source.)
Similarly with format():
>>> '{0:+g}'.format(2.)
'+2'
>>> '{0:+g}'.format(2.1)
'+2.1'
>>> '{0:+g}'.format(2.1001)
'+2.1001'
%+g works but uses (very) old-style formatting. Python has evolved to provide 2 modern formatting methods:
New style formatting:
>>> "{0:+g}".format(2.00001)
'+2.00001'
>>> "{0:+g}".format(-2.00001)
'-2.00001'
>>> "{0:+g}".format(2)
'+2'
and with python 3.6+ f-strings:
>>> value = 2
>>> f"{value:+g}"
'+2'
>>> value = -2.00001
>>> f"{value:+g}"
'-2.00001'
>>> value = 2.00001
>>> f"{value:+g}"
'+2.00001'

Work out significant decimal points to format floating point number

I found this which covers how to suppress scientific notation, but I'd like to go a step further and also work out the formatting string (i.e. the number of required decimal points to represent a number).
My idea so far is to assume a very high resolution (20 in this example) and reduce trailing zeroes as suggested in the accepted answer here. Is there perhaps a better way to achieve this?
>>> f = 0.00000008
>>> s = '{:.20f}'.format(f)
>>> s
'0.00000008000000000000'
>>> s.rstrip('0')
'0.00000008'
Note, I don't want scientific notation either (which you would get with a formatting string of {:g}).
The max number of significant digits in a float is 15 (the power is separate from this). So there's no point in accounting for more digits past that, as they wouldn't be right. Knowing what the power of ten for a given number will tell you how many zeros to pad out in the front.
If what you're formatting will never get above one, then the following will suffice:
from math import log10
def floatformat(f):
pwr = log10(f)
return '{:.0{}f}'.format(f, int(abs(pwr)) + 15).rstrip('0')
But if you're parsing any possible float value, you'll have to handle large numbers a bit differently to get trailing zeros and not random inaccurate digits.
def floatformat(f):
sigs = 15 # number of accurate digits that a float can contain
pwr = log10(f)
if pwr > sigs: # power above accurate digits
s = '{:.{}f}'.format(f / 10 ** int(pwr), sigs)
s = s.replace('.', '') # remove decimal point, no longer necessary here
s = s + '0' * (int(pwr) - sigs) # add in trailing zeros
elif 0 < pwr <= sigs: # power within accurate digits
s = '{:.{}f}'.format(f, sigs - int(pwr)).rstrip('0')
else: # power below accurate digits
s = '{:.0{}f}'.format(f, int(abs(pwr)) + sigs).rstrip('0')
if s[-1] == '.': s = s[:-1] # remove trailing decimal point if needed
return s
All this is doing is keeping the accurate digits, then shuffling them around to have the correct power without the scientific notation.
Examples:
>>> floatformat(0.00000008)
'0.00000008'
>>> floatformat(0.0000000000000000000000000000008)
'0.0000000000000000000000000000008'
>>> floatformat(0.00000000000000000000000000000080067)
'0.00000000000000000000000000000080067'
>>> floatformat(2.31451103e7)
'23145110.3'
>>> floatformat(2.31451103e3)
'2314.51103'
>>> 935.16087e203 == float(floatformat(935.16087e203)) # conversion check to see if power is handled correctly
True
>>> import sys
>>> floatformat(sys.float_info.max)
'179769313486231600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
>>> floatformat(sys.float_info.min)
'0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072'

Is there a more readable or Pythonic way to format a Decimal to 2 places?

What the heck is going on with the syntax to fix a Decimal to two places?
>>> from decimal import Decimal
>>> num = Decimal('1.0')
>>> num.quantize(Decimal(10) ** -2) # seriously?!
Decimal('1.00')
Is there a better way that doesn't look so esoteric at a glance? 'Quantizing a decimal' sounds like technobabble from an episode of Star Trek!
Use string formatting:
>>> from decimal import Decimal
>>> num = Decimal('1.0')
>>> format(num, '.2f')
'1.00'
The format() function applies string formatting to values. Decimal() objects can be formatted like floating point values.
You can also use this to interpolate the formatted decimal value is a larger string:
>>> 'Value of num: {:.2f}'.format(num)
'Value of num: 1.00'
See the format string syntax documentation.
Unless you know exactly what you are doing, expanding the number of significant digits through quantisation is not the way to go; quantisation is the privy of accountancy packages and normally has the aim to round results to fewer significant digits instead.
Quantize is used to set the number of places that are actually held internally within the value, before it is converted to a string. As Martijn points out this is usually done to reduce the number of digits via rounding, but it works just as well going the other way. By specifying the target as a decimal number rather than a number of places, you can make two values match without knowing specifically how many places are in them.
It looks a little less esoteric if you use a decimal value directly instead of trying to calculate it:
num.quantize(Decimal('0.01'))
You can set up some constants to hide the complexity:
places = [Decimal('0.1') ** n for n in range(16)]
num.quantize(places[2])

Format a number containing a decimal point with leading zeroes

I want to format a number with a decimal point in it with leading zeros.
This
>>> '3.3'.zfill(5)
003.3
considers all the digits and even the decimal point. Is there a function in python that considers only the whole part?
I only need to format simple numbers with no more than five decimal places. Also, using %5f seems to consider trailing instead of leading zeros.
Is that what you look for?
>>> "%07.1f" % 2.11
'00002.1'
So according to your comment, I can come up with this one (although not as elegant anymore):
>>> fmt = lambda x : "%04d" % x + str(x%1)[1:]
>>> fmt(3.1)
0003.1
>>> fmt(3.158)
0003.158
I like the new style of formatting.
loop = 2
pause = 2
print 'Begin Loop {0}, {1:06.2f} Seconds Pause'.format(loop, pause)
>>>Begin Loop 2, 0002.1 Seconds Pause
In {1:06.2f}:
1 is the place holder for variable pause
0 indicates to pad with leading zeros
6 total number of characters including the decimal point
2 the precision
f converts integers to floats
print('{0:07.3f}'.format(12.34))
This will have total 7 characters including 3 decimal points, ie. "012.340"
Like this?
>>> '%#05.1f' % 3.3
'003.3'
Starting with a string as your example does, you could write a small function such as this to do what you want:
def zpad(val, n):
bits = val.split('.')
return "%s.%s" % (bits[0].zfill(n), bits[1])
>>> zpad('3.3', 5)
'00003.3'
With Python 3.6+ you can use the fstring method:
f'{3.3:.0f}'[-5:]
>>> '3'
f'{30000.3:.0f}'[-5:]
>>> '30000'
This method will eliminate the fractional component (consider only the whole part) and return up to 5 digits. Two caveats: First, if the whole part is larger than 5 digits, the most significant digits beyond 5 will be removed.
Second, if the fractional component is greater than 0.5, the function will round up.
f'{300000.51:.0f}'[-5:]
>>>'00001'

Categories

Resources