numpy float32 truncating decimal [duplicate] - python

This question already has answers here:
How to set the precision on str(numpy.float64)?
(5 answers)
Closed 7 years ago.
I'm working on a school project that requires me to do some math on single-precision floating point numbers. I thought I would use the float32 format in numpy as python is really the only general purpose language I know. IMO this number format should be able to handle the number 1.0000001, but it keeps truncating my answer to 1.0. The closest I can get it to handle is 1.00001. Can anyone shed any light on this? I'm new to this floating point format and Python.
import numpy as np
keyInput=np.float32(input("Enter a number and i'll float 32 it: "))
print(keyInput)
print(np.float32(keyInput))
print("This is of type: ",type(keyInput))
input('Press ENTER to exit')

First of all, print without explicit formatting or conversion is not reliable. You should try something like print "%.10f" % number instead of print number.
Second, as commentators have pointed out, you can't expect all decimal numbers gets represented precisely as floating point number. Read the Goldberg paper. It's a must read.
An example ipython session for you (I'm using Python 2.7, if you use Python 3, print is a function):
In [1]: import numpy
In [2]: print numpy.float32(1.0 + 1e-7)
1.0
In [3]: print "%.10f" % numpy.float32(1.0 + 1e-7)
1.0000001192
In [4]: print "%.10f" % numpy.float32(1.0 + 1e-8)
1.0000000000
Edit: you can use numpy to inspect type precision limits. Consult the doc of numpy.MachAr for more.
Example:
In [1]: import numpy
In [2]: machar = numpy.MachAr(float_conv=numpy.float32)
In [3]: machar.eps
Out[3]: 1.1920928955078125e-07

Related

Pandas subtraction behavior having precision issues (even after casting) [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 2 years ago.
Maybe this was answered before, but I'm trying to understand what is the best way to work with Pandas subtraction.
import pandas as pd
import random
import numpy as np
random.seed(42)
data = {'r': list([float(random.random()) for i in range(5)])}
for i in range(5):
data['r'].append(float(0.7))
df = pd.DataFrame(data)
If I run the following, I get the expected results:
print(np.sum(df['r'] >= 0.7))
6
However, if I modify slightly the condition, I don't get the expected results:
print(np.sum(df['r']-0.5 >= 0.2))
1
The same happens if I try to fix it by casting into float or np.float64 (and combinations of this), like the following:
print(np.sum(df['r'].astype(np.float64)-np.float64(0.5) >= np.float64(0.2)))
1
For sure I'm not doing the casting properly, but any help on this would be more than welcome!
You're not doing anything improperly. This is a totally straightforward floating point error. It will always happen.
>>> 0.7 >= 0.7
True
>>> (0.7 - 0.5) >= 0.2
False
You have to remember that floating point numbers are represented in binary, so they can only represent sums of powers of 2 with perfect precision. Anything that can't be represented finitely as a sum of powers of two will be subject to error like this.
You can see why by forcing Python to display the full-precision value associated with the literal 0.7:
format(0.7, '.60g')
'0.6999999999999999555910790149937383830547332763671875'
To add to #senderle answer, since this is a floating point issue you can solve it by:
((df['r'] - 0.5) >= 0.19).sum()
Oh a slightly different note, I'm not sure why you use np.sum when you could just use pandas .sum, seems like an unnecessary import

Python 3.5.0 decimal [duplicate]

This question already has answers here:
How to format a floating number to fixed width in Python
(10 answers)
Closed 7 years ago.
So I need to cut off some decimals cases in my output.
I have the following line:
print ("O valor a pagar é de ", ***float(n3)*1.3***, " euros.")
In the area that I highlited I get to much cases... The output is something like this for example: 2,47893698236923 and I want it to show only 2 decimal cases like: 2,47.
How do I do it? And I'm using python 3.5.0
As pointed out by BlivetWidget, floating format causes rounding not truncating.
You can use the Decimal module :
from decimal import Decimal, ROUND_DOWN
x = 2.47893698236923
print(Decimal(str(x)).quantize(Decimal('.01'), rounding=ROUND_DOWN))
print(Decimal(str(x)).quantize(Decimal('.001'), rounding=ROUND_DOWN))
output :
2.47
2.478
EDIT
As the Python docs explains :
The quantize() method rounds a number to a fixed exponent. This method
is useful for monetary applications that often round results to a
fixed number of places
EDIT 2
See also Truncating floats in Python
Use the round() function.
print("O valor a pagar é de ", round(float(n3) * 1.3, 2), " euros.")
Based on the example you gave, you want to truncate a value (the other answers at this time will round the value). If you just want to truncate, I would use a string slice.
>>> num = 2.47893698236923
>>> str(num)[:str(num).find('.') + 3]
'2.47'
PS: if you don't know if there will be a decimal in there, you could use this variant:
>>> numstr = str(num)
>>> numstr[:(numstr.find('.') + 3) if numstr.find('.') else None]
'2.47'

Convert scientific notation to decimals [duplicate]

This question already has answers here:
How to suppress scientific notation when printing float values?
(16 answers)
Closed 7 months ago.
The community reviewed whether to reopen this question 5 months ago and left it closed:
Original close reason(s) were not resolved
I have numbers in a file (so, as strings) in scientific notation, like:
8.99284722486562e-02
but I want to convert them to:
0.08992847
Is there any built-in function or any other way to do it?
I'm pretty sure you can do this with:
float("8.99284722486562e-02")
# and now with 'rounding'
"{:.8f}".format(float("8.99284722486562e-02"))
I'm making this answer since the top voted one has misinformation and so i can explain my improvements.
TL;DR: Use ("%.17f" % n).rstrip('0').rstrip('.')
By default Python formats to scientific notation if there's 5 or more zeroes at the beginning.
0.00001 / 1e-05 formats to "1e-05".
0.0001 / 1e-04 formats to "0.0001".
So of course 8.99284722486562e-02 will format to "0.0899284722486562" already.
A better example would've been 8.99284722486562e-05. (0.00008992847224866)
We can easily format to raw decimal places with "%f" which is same as "%.6f" by default.
"%f" % 8.99284722486562e-05 produces '0.000090'.
"%f" % 0.01 produces '0.010000'.
By default floats display upto 17 decimal places.
0.1234567898765432123 - (19 dp input)
0.12345678987654321 - (17 dp output)
So if we did "%.17f" % 8.99284722486562e-02 we'd get '0.08992847224865620'. (note the extra 0)
But if we did "%.17f" % 0.0001 we surely wouldn't want '0.00010000000000000'.
So to remove the trailing zeroes we can do: ("%.17f" % n).rstrip('0').rstrip('.')
(Notice we also strip the decimal point incase the number has no fraction left)
Also there's counterparts to %f:
%f shows standard notation
%e shows scientific notation
%g shows default (scientific if 5 or more zeroes)
The scientific notation can be converted to a floating point number with float.
   In [1]:  float("8.99284722486562e-02")
Out [1]:   0.0899284722486562
The float can be rounded with format and then float can be used on the string to return the final rounded float.
   In [2]:  float("{:.8f}".format(float("8.99284722486562e-02")))
Out [2]:   0.08992847
2022 edit for No Sound's comment:
I learned this solution from here (archived)
The following solutions work for larger numbers.
Solution 1)
import numpy as np
print(np.format_float_positional(1.32456e-12, trim='-'))
print(np.format_float_positional(1.32456e-24, trim='-'))
print(np.format_float_positional(1.32456e12, trim='-'))
print(np.format_float_positional(1.32456e24, trim='-'))
# Output: 0.00000000000132456
# 0.00000000000000000000000132456
# 1324560000000
# 1324560000000000000000000
Solution 2)
same as above accept this time using a lambda function
import numpy as np
pretty_print = lambda x: np.format_float_positional(x, trim="-")
print(pretty_print(1.32456e-12))
print(pretty_print(1.32456e-24))
print(pretty_print(1.32456e12))
print(pretty_print(1.32456e24))
# Output: 0.00000000000132456
# 0.00000000000000000000000132456
# 1324560000000
# 1324560000000000000000000
As you may know floating point numbers have precision problems. For example, evaluate:
>>> (0.1 + 0.1 + 0.1) == 0.3
False
Instead you may want to use the Decimal class. At the python interpreter:
>>> import decimal
>>> tmp = decimal.Decimal('8.99284722486562e-02')
Decimal('0.0899284722486562')
>>> decimal.getcontext().prec = 7
>>> decimal.getcontext().create_decimal(tmp)
Decimal('0.08992847')

Rounding logic in Python? [duplicate]

This question already has answers here:
Python float to int conversion
(6 answers)
Closed 8 years ago.
In my original code I was trying to compute some indices out of some float values and I faced the following problem:
>>> print int((1.40-.3)/.05)
21
But:
>>> print ((1.40-.3)/.05)
22.0
I am speechless about what is going on. Can somebody please explain?
This is caused by floating point inaccuracy:
>>> print repr((1.40-.3)/.05)
21.999999999999996
You could try using the Decimal type instead:
>>> from decimal import Decimal
>>> Decimal
<class 'decimal.Decimal'>
and then
>>> (Decimal('1.40') - Decimal('.3')) / Decimal('.05')
Decimal('22')
The fractions.Fraction class would work too. Or, you could just round:
>>> round((1.40-.3)/.05, 10) # round to 10 decimal places
22.0
Drop the print and you'll see that the actual value is:
>>> (1.40-.3)/.05
21.999999999999996
Python 2 print() (more accurately, float.__str__) lies to you by rounding to a couple of decimal digits. Python 3 print() (again, actually float.__str__) doesn't do that, it always gives a faithful representation of the actual value (it abbreviates, but only when it doesn't change the value).
This inaccuracy is inherent to floating point numbers (including Decimal, though its inaccuracies occur different cases). This is a fundamental problem, representing arbitrary real numbers is not possible. See Is floating point math broken? for explanations.
I think this explains it straightforwardly:
import decimal
>>> (decimal.Decimal(1.40) -decimal.Decimal(.3))/decimal.Decimal(.05)
Decimal('21.99999999999999722444243843')
>>> (decimal.Decimal('1.40') -decimal.Decimal('.3'))/decimal.Decimal('.05')
Decimal('22')

Number changing when inserted into list [duplicate]

This question already has answers here:
python floating number [duplicate]
(4 answers)
Closed 8 years ago.
I'm only going to paste part of my code since it's very long, but I was wondering if any one might know potential causes of this problem. So I have this code here:
print "part a", working_weight
cells[working_cell_position][4] = working_weight
print "part b", working_weight, cells[working_cell_position][4], cells[working_cell_position]
and what it prints is this:
part a 62.4
part b 62.4 62.4 [6, 6, '', '', 62.400000000000006]
So if you didn't quite get it, basically I have the variable working_weight which is 62.4, but when I insert it into a list, it changes it to 62.400000000000006, yet if I only print that number from the list it prints as 62.4 still. If anyone could help, or suggest a solution to fix this it would be greatly appreciated.
This is because floats are inherently imprecise in pretty much every language, as they cannot be represented easily in 64-bit binary at the lowest level. What is happening to your code has nothing to do with how it is put into the list or anything like that.
If you want to keep a precise decimal you should use decimal.Decimal.
>>> from decimal import Decimal
>>> working_weight = Decimal(str(working_weight))
>>> working_weight
Decimal('62.4')
This Decimal can then have operations performed on it like any float.
Floating point math is tough, that's the problem. If you have decimals, use decimal.Decimal.
from decimal import Decimal
a = Decimal("30")
b = Decimal("32.4")
print(a+b)
# Decimal ('62.4')
It's hard to say for sure without having a reproducible problem, but one way to get something similar is for example
import numpy
a = numpy.array([62.4], numpy.float)
print a[0] # outputs 62.4
print [a[0]] # outputs [62.399999999999999]
where is working_height coming from? What is working_height.__class__?

Categories

Resources