Round 2 digit after decimal point after zero in Python - python

Assume I have a float:
x = 0.0005953829144211724
I have to round it after the decimal to:
x = 0.00059
Similarly, if
x = 0.00000046605219739046376
then the result should be
x = 0.00000046
Is there any inbuild function in python to do this?

You can use a nested format with Decimal. The first format does the rounding using the "g" specifier. The second one prints all the digits, without scientific notation, using the decimal value of the rounded string:
from decimal import Decimal
x = 0.0005953829144211724
print(f"{Decimal(f'{x:.2g}'):f}") # 0.0006
print(f"{Decimal(f'{x:.3g}'):f}") # 0.000595
print(f"{Decimal(f'{x:.4g}'):f}") # 0.0005954
x = 0.00000046605219739046376
print(f"{Decimal(f'{x:.2g}'):f}") # 0.00000047
print(f"{Decimal(f'{x:.3g}'):f}") # 0.000000466
print(f"{Decimal(f'{x:.4g}'):f}") # 0.0000004661
Note that this DOES round the value to the specified precision, contrary to your examples which truncate the mantissa instead of rounding it

As shown in the other answer, if you want a certain number of significant digits, you should format the number in scientific notation. If, however, you want those significant digits in the "normal" format, you might either convert that scientific notation back to float (thus "forgetting" all the "insignificant" digits) and then back to string and rstrip all excess zeros, or maybe use a regular expression:
>>> x = 0.00000046605219739046376
>>> f'{float(f"{x:.2g}"):.20f}'.rstrip("0")
'0.00000047'
>>> re.match(r"0\.0*[^0]{2}", f"{x:.20f}").group()
'0.00000046'
Note: i) The .20f here means "print in normal decimal format with 20 places after the decimal", where the 20 is kind of arbitrary. ii) The regex will not round but just trim the number.

You can do something close with the g specifier in an f-string:
x = 0.00000046605219739046376
print(f'{x:.2g}')
This will print the result in "scientific notation"
4.7e-07
Similarly:
x = 0.0005953829144211724
print(f'{x:.2g}')
results in
0.0006
since it rounds up.

Related

Displaying floats using F-string

I'm really curious about why the behaviour is so different when add this "f" to the end of the number which I want to display:
# CASE with f
float1 = 10.1444786
print(f"{float1:.4f}")
# print out: 10.1445
# CASE without f
print(f"{float1:.4}")
# print out: 10.14
Why only 2 characters are displayed in the second example?
The implied type specifier is g, as given in the documentation Thanks #Barmar for adding a comment with this info!
None: For float this is the same as 'g', except that when fixed-point notation is used to format the result, it always includes at least one digit past the decimal point. The precision used is as large as needed to represent the given value faithfully.
For Decimal, this is the same as either 'g' or 'G' depending on the value of context.capitals for the current decimal context.
The overall effect is to match the output of str() as altered by the other format modifiers.
An experiment:
for _ in range(10000):
r = random.random() * random.randint(1, 10)
assert f"{r:.6}" == f"{r:.6g}"
Works every time
From https://docs.python.org/3/library/string.html#formatstrings,
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. A precision of 0 is treated as equivalent to a precision of 1.
So in your second example, you ask for 4 sigfigs, but in your first you ask for 4 digits of precision.

Number of digits after decimal point in scientific (e) format

I want to get desired number of digits after decimal point while keeping answer in scientific format (e.g. 2.989e+10). I know the method format: "{:0.Ae}".format(given_number) where A is number of digits after decimal. However, I'm getting number of digits after the decimal (A) through a variable.
Can someone please help me how can I implement it to get the desired result?
My code: Consider A as number of digits after decimal point.
for val in [1.049666666666667e-08, 4.248944444444444e+05]:
val_log = math.log10(val);
val_e = round(val_log - (0.5 if val_log<0 else 0));
A = abs(val_e +3);
valstr = "{:0.Ae}".format(val)
print(valstr)
It is basically number of digits after decimal point(A) = |3 + the value after e|. How should I use value of A in {:0.xe}.format(val) ?
You can nest a variable in the precision field, as the documentation of formatted string literals points out in an example with the comment "nested fields".
Below is a more simplified example that limits the precision to 3 digits (or 2 digits after the decimal point):
>>> f = 123.12345
>>> n = 3
>>> print(f'{f:.{n}}')
1.23e+02
>>>

Why is implicitly rounding up a number no matter what dtype i use?

I have been trying to fix the precision issue in my code that has been breaking. I want the value to be presented exactly as i provided but it seems like Python is rounding up the number. Below is the sample.
x = 3069682093880544.81
print (x)
3069682093880545.0
x = Decimal(3069682093880544.81)
print (x)
3069682093880545
x = float(3069682093880544.81)
print(x)
3069682093880545.0
x = Decimal(str(3069682093880544.81))
print(x)
3069682093880545.0
3069682093880545.0
x = str(3069682093880544.81)
print(x)
3069682093880545.0
All i want is to be able to assign exact value to the variable and it provides me the same value when called. What am i doing wrong?
The number 3069682093880544.81 is being converted into a 64 bit floating point number according the IEEE format. The closest number in that format is 43910A47D717A69F. However, converting that number back will be 3069682093880544.64. As you can see, the last 2 digits after the comma have changed.
The number of significant digits in a IEEE 64 bit float is 16 digits. And that's likely why the printed output choses to stop printing after 16 digits, which is 3069682093880545.
If you want more decimal places, you need to chose a method which does not have a IEEE floating point number in the way of its processing. (Note that even the source code interpreter will parse numbers into floating point format already.) As mentioned by #LeopardShark,
from decimal import *
print(Decimal("3069682093880544.81"))
goes from String to Decimal without any processing as float.
The problem is that the literal 3069682093880544.81 is parsed as a float. So, your second statement, for example, is sort of equivalent to Decimal(float(3069682093880544.81)). What you want is Decimal("3069682093880544.81") which parses it as a string, and then converts it to a Decimal.

python float to string without precision loss

For python 3 I want to convert a float to a string, with possibly different length (i.e. number of digits) but with full precision.
Also I need to have a decimal point in any case:
1 -> '1.'
1/10 -> '0.1000000000000000055511151231257827021181583404541015625'
currently my code is this:
from decimal import Decimal
def formatMostSignificantDigits(x):
out = str(Decimal(x))
if out.find('.') < 0:
out += '.'
return out
can this be done more elegantly? (e notation would be possible, too)
Use Pythons string formatting functions:
>>> x = 1.0; '{:.55f}'.format(x)
'1.0000000000000000000000000000000000000000000000000000000'
>>> x = 1/10; '{:.55f}'.format(x)
'0.1000000000000000055511151231257827021181583404541015625'
If you want to be able to feed it integers (such as 1) as well, use '{:.55f}'.format(float(x)).
If you want to strip any trailing zeroes, use '{:.55f}'.format(x).rstrip('0').
Note that 55 decimals after the point is way overkill (but it's what you showed in your question); 16 digits should suffice to express the full precision of double-precision IEEE 754 floats (20 digits for the 80-bit extended-precision you might encounter).
why do you use Decimal, you can just use:
x = 0.1
s = str(x)
print(s) # this prints '0.1'
but if you use Decimal for something else than instead of this:
out = str(Decimal(x))
if out.find('.') < 0:
out += '.'
return out
you can just use:
return Decimal(x).__str__()
Edit 1:
also good module for float precision is bigfloat:
from bigfloat import BigFloat
x = 0.1
print(BigFloat(x, precision(300)).__str__())
# thsi will print'0.10000000000000000555111512312578270211815834045410156250000000000000000000000000000000000000'
# but if you use this:
print(BigFloat(x.__str__(), precision(300)).__str__())
# it can be precise as much as you want
print(BigFloat(x.__str__(), precision(100000)).__str__()) # try this

Convert number to string scientific notation fixed length

I have a normal float number such as "1234.567" or "100000". I would like to convert it to a string such that the precision is fixed and the number is in scientific notation. For example, with 5 digits, the results would be "1.2346e003 and "1.0000e005", respectively. The builtin Decimal number -> string functions will round it if it can, so the second number would be only "1e005" even when I want more digits. This is undesirable since I need all numbers to be the same length.
Is there a "pythonic" way to do this without resorting to complicated string operations?
precision = 2
number_to_convert = 10000
print "%0.*e"%(precision,number_to_convert)
is that what you are asking for?
You can use the %e string formatter:
>>> '%1.5e'%1234.567
'1.23457e+03'
>>> "%1.5e"%100000
'1.00000e+05'
%x.ye where x = min characters and y = max precision.
If you need to keep the 3-digit exponent like in your example, you can define your own function. Here's an example adapted from this answer:
def eformat(f, prec, exp_digits):
s = "%.*e"%(prec, f)
mantissa, exp = s.split('e')
return "%se%0*d"%(mantissa, exp_digits, int(exp))
>>> print eformat(1234.567, 4, 3)
1.2346e003

Categories

Resources