Python Floating Point Formatting - python

I've seen a few questions about this already, but none that I read helped me actually understand why what I am trying to do is failing.
So I have a bunch of floating point values, and they have different precisions. Some are 0.1 others are 1.759374, etc. And I want to format them so they are ALL in the form of "+0.0000000E+00" I tried doing
number = '%1.7f' % oldnumber
but that didn't work. I thought what I was telling it to do was "one digit perfor the decimal point, and 7 after, float" but it doesn't work. I'm not really getting the examples in the docs, which don't seem to even bother with "before and after decimal point" issues, and I didn't find a question that was about before and after decimal point fixing.
Now, I know that some of my numbers are 0.0437 or similar, and I want them to appear as 4.3700000E-02 or something. I was sort of hoping it would do the E bit on it's own, but if it doesn't how do I do it?
Here is the exact line I have:
RealValConv = '%1.7g' % struct.unpack('!f', RealVal.decode('hex'))[0]
RealVal is a hex number that represents the value I want.
Also, this is in Python 2.7

>>> '{:.7e}'.format(0.00000000000000365913456789)
'3.6591346e-15'

You can use the scientific notation format: Something like this:
number = '%e' % oldnumber
>>> x = 1.759374
>>> print '%e' % x
1.759374e+00
>>>
>>> x = 1.79
>>> print '%e' % x
1.790000e+00
>>>
>>> x = 1.798775655
>>> print '%e' % x
1.798776e+00
>>>
Or, if you want to control precision, you can use the format method as sugged by #leon approach (+1).
>>> x = 1.759374
>>>
>>> print('{:.2e}'.format(x))
1.76e+00
>>>
>>> print('{:.10e}'.format(x))
1.7593740000e+00
>>>
>>> print('{:.4e}'.format(x))
1.7594e+00
>>>

Related

Python Prevent Rounding Error with floats

I have checked dozens of other questions on stackoverflow about rounding floats in Python. I have learned a lot about how computers store these numbers, which is the root cause of the issue. However, I have not found a solution for my situation.
Imagine the following situation:
amount = 0.053
withdraw = 0.00547849
print(amount - withdraw)
>>> 0.047521509999999996
Here, I would actually like to receive 0.04752151, so a more rounded version.
However, I do not know the number of decimals these numbers should be rounded upon.
If I knew the number of decimals, I could do the following:
num_of_decimals = 8
round((amount - withdraw),num_of_decimals)
>>> 0.04752151
However, I do not know this parameter num_of_decimals. It could be 8,5,10,2,..
Any advice?
When decimals are important, then Python offers a decimal module that works best with correct rounding.
from decimal import Decimal
amount = Decimal(0.053)
withdraw = Decimal(0.00547849)
print(amount - withdraw)
# 0.04752150999999999857886789911
num_of_decimals = 8
print(round(amount - withdraw,num_of_decimals))
# 0.04752151
Update: Get number of decimal
num_of_decimals = (amount - withdraw).as_tuple().exponent * -1
#or find
num_of_decimals = str(amount - withdraw)[::-1].find('.')
Instead of round
from decimal import getcontext
...
getcontext().prec = num_of_decimals
print(amount - withdraw)
# 0.047521510
See more decimal documentation
Use decimal with string inputs:
from decimal import Decimal
amount = Decimal("0.053")
withdraw = Decimal("0.00547849")
print(amount - withdraw)
# 0.04752151
Avoid creating floats entirely, and avoid the rounding issues.
If you end up with numbers small enough, and the default representation is now something like 1.2345765645033E-8, you can always get back to decimal notation in your representation:
>>> withdraw = Decimal("0.000000012345765645033")
>>> '{0:f}'.format(withdraw)
'0.000000012345765645033'
Concentrating purely on the number of decimal places, something simple, like converting the number to a string might help.
>>> x = 0.00547849
>>> len(str(x).split('.')[1])
8
>>> x = 103.323
>>> len(str(x).split('.')[1])
3
It appears that I've offended the gods by offering a dirty but practical solution but here goes nothing, digging myself deeper.
If the number drops into scientific notation via the str function you won't get an accurate answer.
Picking the bones out of '23456e-05' isn't simple.
Sadly, the decimal solution also falls at this hurdle, once the number becomes small enough e.g.
>>> withdraw = decimal.Decimal('0.000000123456')
>>> withdraw
Decimal('1.23456E-7')
Also it's not clear how you'd go about getting the string for input into the decimal function, because if you pump in a float, the result can be bonkers, not to put too fine a point on it.
>>> withdraw = decimal.Decimal(0.000000123456)
>>> withdraw
Decimal('1.23455999999999990576323642514633416311653490993194282054901123046875E-7')
Perhaps I'm just displaying my ignorance.
So roll the dice, as to your preferred solution, being aware of the caveats.
It's a bit of a minefield.
Last attempt at making this even more hacky, whilst covering the bases, with a default mantissa length, in case it all goes horribly wrong:
>>> def mantissa(n):
... res_type = "Ok"
... try:
... x = str(n).split('.')[1]
... if x.isnumeric():
... res = len(x)
... else:
... res_type = "Scientific notation "+x
... res = 4
... except Exception as e:
... res_type = "Scientific notation "+str(n)
... res = 4
... return res, res_type
...
>>> print(mantissa(0.11))
(2, 'Ok')
>>> print(mantissa(0.0001))
(4, 'Ok')
>>> print(mantissa(0.0001234567890))
(12, 'Ok')
>>> print(mantissa(0.0001234567890123456789))
(20, 'Ok')
>>> print(mantissa(0.00001))
(4, 'Scientific notation 1e-05')
>>> print(mantissa(0.0000123456789))
(4, 'Scientific notation 23456789e-05')
>>> print(mantissa(0.0010123456789))
(13, 'Ok')

How to set the number of decimal places in scientific notation?

I am working on python 2.7. I want to fix the number of decimal places in a number written in scientific notation like 1.32e6, but instead of using for example "%3f". I want to write something like:
n=3
"%.nf"
where n is the number of decimal places, but it can be changed from my application.
n = 4
print("{0:.{1}f}".format(1.987213,n))
n = 5
print("{0:.{1}f}".format(1.987213,n))
Output
1.9872
1.98721
i = 123456789
n = 3
print('{:.{n}e}'.format(i, n=n))
Output:
'1.235e+08'
You can try this,
>>> a = 10e6
>>> n = 2
>>> '%.{}f'.format(n) % a
'10000000.00'
Or cast to float,
>>> a = 10e6
>>> n = 2
>>> float('%.{}f'.format(n) % a)
10000000.0
If you want to return to scientific notation,
>>> a = 103.023232
>>> n = 3
>>> '%.{}E'.format(n) % a
'1.030E+02'
The old printf-style formatting operator supports this, though you'll probably want to use the newer format method.
>>> n = 3
>>> "%.*f" % (n, 3.14159)
'3.142'
When you use * as the precision, the value to use precedes the floating-point value in the tuple on the RHS.
Thank you every body for the answers. I tried the method of Ajay Dabas and that worked for me perfectly. This example is going to be used in a major (but easy) application. The complete code with the improved solution is:
enter code hereentrada_n= "Ingrese n:"
enter code heren= input (entrada_n)
enter code hereentrada_numero="Ingrese numero:"
enter code herenumero=input(entrada_numero)
enter code hereprint("{0:.{1}f}".format(numero,n))
enter code hereprint("{0:.{1}e}".format(numero,n))

Error with Python modulo on scientific notation

I hope someone can help me with an explanation for this strange result on large divisor, or suggest me with some keywords so can I get a better search.
>>> m = 1e9+9
>>> n = 1000000009
>>> m == n
True
>>> 2549015908609065 % m
885667930.0
>>> 2549015908609065 % n
885667930
>>> 2549015908609065L % n
885667930L
>>> 2549015908609065L % m
885667930.0
>>> 254901590860906524041412370460835L % m
98506080.0
>>> 254901590860906524041412370460835L % n
327998297L
>>> 254901590860906524041412370460835L % int(m)
327998297L
The reason you are seeing strange results is because you are performing modulo on floating point numbers, which have an inexact representation. The documentation on the decimal module highlights this quite well.
To perform exact operations on very large number, you can use the decimal class as follows:
from decimal import *
print Decimal(2549015908609065) % Decimal(1e9) # Gives the answer you expect
m = 1e9+9 stores the number as a float; however n = 1000000009 stores the numbers as an integer.
When you divide an integer number by a float, python implicitly outputs a float as a result; However, when you divide an integer by another integer, you get the integral quotient.
The precision of Float over bigger numbers gets reduced.
If you notice,
>>> 254901590860906524041412370460835L % m
98506080.0
#outputted a float with different precision
>>> 254901590860906524041412370460835L % n
327998297L
#outputted a long int
Check http://www.tutorialspoint.com/python/python_numbers.htm for a basic tutorial on python numbers

Sum of two "np.longdouble"s yields big numerical error

Good morning,
I'm reading two numbers from a FITS file (representing the integer and floating point parts of a single number), converting them to long doubles (128 bit in my machine), and then summing them up.
The result is not as precise as I would expect from using 128-bit floats. Here is the code:
a_int = np.longdouble(read_header_key(fits_file, 'I'))
print "I %.25f" % a_int, type(a_int)
a_float = np.longdouble(read_header_key(fits_file, 'F'))
print "F %.25f" % a_float, a_float.dtype
a = a_int + a_float
print "TOT %.25f" % a, a.dtype
and here's the answer I get:
I 55197.0000000000000000000000000 <type 'numpy.float128'>
F 0.0007660185200000000195833 float128
TOT 55197.0007660185219720005989075 float128
The result departs from what I would expect(55197.0007660185200000000195833) after just 11 decimal digits (16 significant digits in total). I would expect a much better precision from 128bit floats. What am I doing wrong?
This result was reproduced on a Mac machine and on a Linux 32bit machine (in that case, the dtype was float96, but the values were exactly the same)
Thanks in advance for your help!
Matteo
The problem lies in your printing of the np.longdouble. When you format using %f, Python casts the result to a float (64-bits) before printing.
Here:
>>> a_int = np.longdouble(55197)
>>> a_float = np.longdouble(76601852) / 10**11
>>> b = a_int + a_float
>>> '%.25f' % b
'55197.0007660185219720005989075'
>>> '%.25f' % float(b)
'55197.0007660185219720005989075'
>>> b * 10**18
5.5197000766018519998e+22
Note that on my machine, I only get a bit more precision with longdouble compared with ordinary double (20 decimal places instead of 15). So, it may be worth seeing if the Decimal module might be more suited for your application. Decimal handles arbitrary-precision decimal floating-point numbers with no loss of precision.
My guess is that the %f modifier constructs a float from your longdouble object and uses that when creating the format string.
>>> import numpy as np
>>> np.longdouble(55197)
55197.0
>>> a = np.longdouble(55197)
>>> b = np.longdouble(0.0007660185200000000195833)
>>> a
55197.0
>>> b
0.00076601852000000001958
>>> a + b
55197.00076601852
>>> type(a+b)
<type 'numpy.float128'>
>>> a + b == 55197.00076601852
False
As a side note, even repr doesn't print enough digets to reconstruct the object. This is simply because you can't have a float literal which is sufficient to pass to your longdouble.

Displaying the output of a variable to more than 2 decimal places

I'm trying to display the output of my addition to more than 2 decimal places.
import time
import random
max_number = 1000000.0
random_time = random.randrange(1, max_number-1) / max_number
range_key = int(time.time()) + random_time
range_key
>>> 1347053222.790799
print range_key
>>> 1347053222.79
How can I print the full number?
If this were a function, how could I return the full number?
When turning a float into a string (printing does this automatically), python limits it to only the 12 most significant digits, plus the decimal point.
When returning the number from a function, you always get the full precision.
To print more digits (if available), use string formatting:
print '%f' % range_key # prints 1347053958.526874
That defaults to 6 digits, but you can specify more precision:
print '%.10f' % range_key # prints 1347053958.5268740654
Alternatively, python offers a newer string formatting method too:
print '{0:.10f}'.format(range_key) # prints 1347053958.5268740654
You already have the full number. Use a custom format specifier on output.
>>> print '%.15f' % range_key
1347053222.790798902511597
You'll find that if you hard-code the number of digits to print, you'll sometimes get too few or worse yet go past the floating-point precision and get meaningless digits at the end. The standard floating point number only has precision to about 15 digits. You can find out how many of those digits are to the right of the decimal point with this simple formula:
digits_right = 14 - int(math.log10(value))
Using that you can create an appropriate %f format:
fmt = '%%0.%df' % max(0, digits_right)
>>> f = 1/7.
>>> repr(f)
'0.14285714285714285'
>>> str(f)
'0.142857142857'
>>> f
0.14285714285714285
>>> print f
0.142857142857
>>> '%.15f' % f
'0.142857142857143'

Categories