Wrong code or strange math behavior? (python 3) - python

I'm calculating with variables by multiplicating etc
and I noticed this strange behavior.
If I use these calculations:
CD = 6
CDR = 0.4
CD = float(CD) - (float(CDR) * float(CD))
Theoretically that would be 6 - (6 * 0.4) = 6 - 2.4 = 3.6 if I then print(CD) it prints
3.5999999999999996
Is there a reason for this which I can avoid? Is there a way, like math.ceil to round up the number but to a certain decimal, for example to x.xxxxx (5th deciamal)?
(Let me know if I did anything wrong in this post, I find answers on this site since a while but have never posted before so I maybe did something wrong, apologies in advance.)

you can try the decimal module, but under the hood your answer is still "correct". It's just how floating point numbers convert to decimal representations.

You're running in to floating point arithmetic problems. Trying using decimal.Decimal instead of float.

If it's for display only (or piece of mind) you can do
x = math.ceil(x*100000.0) / 100000.0
However there's no guarantee that the will be a number that can be represented exactly in memory either (ending up with the same 3.599999999..)

Related

Mitigating Floating Point Approximation Issues with Numpy

My code is quite simple, and only 1 line is causing an issue:
np.tan(np.radians(rotation))
Instead of my expected output for rotation = 45 as 1, I get 0.9999999999999999. I understand that 0 and a ton of 9's is 1. In my use case, however, it seems like the type of thing that will definitely build up over iterations.
What is causing the floating point error: np.tan or np.radians, and how do I get the problem function to come out correctly regardless of floating point inaccuracies?
Edit:
I should clarify that I am familiar with floating point inaccuracies. My concern is that as that number gets multiplied, added, and compared, the 1e-6 error suddenly becomes a tangible issue. I've normally been able to safely ignore floating point issues, but now I am far more concerned about the build up of error. I would like to reduce the possibility of such an error.
Edit 2:
My current solution is to just round to 8 decimal places because that's most likely enough. It's sort of a temporary solution because I'd much prefer a way to get around the IEEE decimal representations.
What is causing the floating point error: np.tan or np.radians, and how do I get the problem function to come out correctly regardless of floating point inaccuracies?
Both functions incur rounding error, since in neither case is the exact result representable in floating point.
My current solution is to just round to 8 decimal places because that's most likely enough. It's sort of a temporary solution because I'd much prefer a way to get around the IEEE decimal representations.
The problem has nothing to do with decimal representation, and this will give worse results outside of the exact case you mention above, e.g.
>>> np.tan(np.radians(60))
1.7320508075688767
>>> round(np.tan(np.radians(60)), 8)
1.73205081
>>> np.sqrt(3) # sqrt is correctly rounded, so this is the closest float to the true result
1.7320508075688772
If you absolutely need higher accuracy than the 15 decimal digits you would get from code above, then you can use an arbitrary precision library like gmpy2.
Take a look here: https://docs.scipy.org/doc/numpy/user/basics.types.html .
Standard dtypes in numpy do not go beyond 64 bits precision. From the docs:
Be warned that even if np.longdouble offers more precision than python
float, it is easy to lose that extra precision, since python often
forces values to pass through float. For example, the % formatting
operator requires its arguments to be converted to standard python
types, and it is therefore impossible to preserve extended precision
even if many decimal places are requested. It can be useful to test
your code with the value 1 + np.finfo(np.longdouble).eps.
You can increase precision with np.longdouble, but this is platform dependent
In spyder (windows):
np.finfo(np.longdouble).eps #same precision as float
>> 2.220446049250313e-16
np.finfo(np.longdouble).precision
>> 15
In google colab:
np.finfo(np.longdouble).eps #larger precision
>> 1.084202172485504434e-19
np.finfo(np.longdouble).precision
>> 18
print(np.tan(np.radians(45, dtype=np.float), dtype=np.float) - 1)
print(np.tan(np.radians(45, dtype=np.longfloat), dtype=np.longfloat) - 1)
>> -1.1102230246251565e-16
0.0

1 == 2 for large numbers of 1

I'm wondering what causes this behaviour. I haven't been able to find an answer that covers this. It is probably something simple and obvious, but it is not to me. I am using python 2.7.3 in Ubuntu.
In [1]: 2 == 1.9999999999999999
Out[1]: True
In [2]: 2 == 1.999999999999999
Out[2]: False
EDIT:
To clarify my question. Is there a written(in documentation) max number of 9's where python will evaluate the expression above as being equal to 2?
Python uses floating point representation
What a floating point actually is, is a fixed-width binary number (called the "significand") plus a small integer to tell you how many powers of two to shift that value by (the "exponent"). Plus a sign bit. Just like scientific notation, but in base 2 instead of 10.
The closest 64 bit floating point value to 1.9999999999999999 is 2.0, because 64 bit floating point values (so-called "double precision") uses 52 bits of significand, which is equivalent to about 15 decimal places. So the literal 1.9999999999999999 is just another way of writing 2.0. However, the closest value to 1.999999999999999 is less than 2.0 (I think it's 1.9999999999999988897769753748434595763683319091796875 exactly, but I'm too lazy to check that's correct, I'm just relying on Python's formatting code to be exact).
I don't actually know whether the use specifically of 64 bit floats is required by the Python language, or is an implementation detail of CPython. But whatever size is used, the important thing is not specifically the number of decimal places, it is where the closest floating-point value of that size lies to your decimal literal. It will be closer for some literals than others.
Hence, 1.9999999999999999 == 2 for the same reason that 2.0 == 2 (Python allows mixed-type numeric operations including comparison, and the integer 2 is equal to the float 2.0). Whereas 1.999999999999999 != 2.
Types coercion
>>> 2 == 2.0
True
And consequences of maximum number of digits that can be represented in python :
>>> import sys
>>> sys.float_info.dig
15
>>> 1.9999999999999999
2.0
more from docs
>>> float('9876543211234567')
9876543211234568.0
note ..68 on the end instead of expected ..67
This is due to the way floats are implemented in Python. To keep it short and simple: Since floats almost always are an approximation and thus have more digits than most people find useful, the Python interpreter displays a rounded value.
More detailed, floats are stored in binary. This means that they're stored as fractions to the base 2, unlike decimal, were you can display a float as fractions to the base 10. However, most decimal fractions don't have an exact representation in binary. Because of that, they are typically stored with a precision of 53 bits. This renders them pretty much useless if you want to do more complex arithmetic operations, since you'll run into some strange problems, e. g.:
>>> 0.1 + 0.2
0.30000000000000004
>>> round(2.675, 2)
2.67
See The docs on floats as well.
Mathematically speaking, 2.0 does equal 1.9999... forever. They are two different ways of writing the same number.
However, in software, it's important to never compare two floats or decimals for equality - instead, subtract them, take the absolute value, and verify that the (always positive) difference is sufficiently low for your purposes.
EG:
if abs(value1 - value2) < 1e10:
# they are close enough
else:
# they are not
You probably should set EPSILON = 1e10, and use the symbolic constant instead of scattering 1e10 throughout your code, or better still use a comparison function.

Python floating point issue? [duplicate]

This question already has answers here:
Python rounding error with float numbers [duplicate]
(2 answers)
Python Float rounding errors [duplicate]
(3 answers)
Closed 9 years ago.
Verison 2.6.5 if it helps.
So, I'm doing some stuff in django and grabbing a value from a Floatfield. And comparing it to another floatfield. Let's call these 2 X and Y.
X eventually ends up as 35.0 when I run it.
Y eventually ends up as 35.0 when I run it.
35.0 > 35.0 # False, ok no worries
X > Y # True, why?
round(X) > round(Y) # False, ok looks like i have to round it
I know float has issues during calculation but in this case, the values for my two variables were displayed as 35.0 so I thought they actually were 35.0. Does this mean python might not show some information behind the float object? I know this is a common question but if it were actually 35.000000001 or something, does python not display that entire value?
Edit: OK, finally read through this. Floating point life problems solved for me now lol. http://docs.python.org/2/tutorial/floatingpoint.html
Python sometimes rounds off floating point values when printing/representing them,* but does not do so while comparing them.
Try this:
'{:.17f} {:.17f}'.format(X, Y)
(Since floats have just over 17 significant digits of precision, and your number is in the range [10, 100), printing 17 digits should be enough to see all of the details that exist. But if you're not sure how to calculate it, just toss on some extra digits: {:.64f} is perfectly legal.)
Alternatively, you could convert them to Decimal objects with a high-enough precision to represent the difference, or many other things.
Or, most simply, X > Y is a good test for whether X is greater than Y, if that's all you actually want to know.
For real-life code, if you want to know if two float values that resulted from two different computations are "the same", you can't do that. Ideally, you need to do proper error analysis. Failing that, an epsilon calculation (abs(X-Y) < EPSILON if you care about a fixed scale, or something like abs((X-Y) / (X+Y)) < EPSILON if you have no idea what the scale might be) is often good enough. But X == Y is never good enough.
* The details differ between different Python versions. For example, try this:
>>> str(1.000000000000001), repr(1.000000000000001)
In Python 2.7, you will get:
'1.0', '1.000000000000001'
But in Python 3.3:
'1.000000000000001', '1.000000000000001'
Apart from simple tests against known-exact literals like 0, do not do simple comparisons between floats.
Always subtract and test to see of the absolute value of the difference is below epsilon. If it is, treat the two floats as equal. If the difference is greater than epsilon, use its sign to determine which is greater than the other.

Python Shell - "Extras" in float subtraction [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Floating Point Limitations
Using Python 2.7 here.
Can someone explain why this happens in the shell?
>>> 5.2-5.0
0.20000000000000018
Searching yielded things about different scales of numbers not producing the right results (a very small number and a very large number), but that seemed pretty general, and considering the numbers I'm using are of the same scale, I don't think that's why this happens.
EDIT: I suppose I didn't define that the "this thing happening" I meant was that it returns 0.2 ... 018 instead of simply resulting in 0.2. I get that print rounds, and removed the print part in the code snippet, as that was misleading.
You need to understand that 5.2-5.0 really is 0.20000000000000018, not 0.2. The standard explanation for this is found in What Every Computer Scientist Should Know About Floating-Point Arithmetic.
If you don't want to read all of that, just accept that 5.2, 5.0, and 0.20000000000000018 are all just approximations, as close as the computer can get to the numbers you really way.
Python has some tricks to allow you to not know what every computer scientist should know and still get away with it. The main trick is that str(f)—that is, the human-readable rendition of a floating-point number—is truncated to 12 significant digits, so str(5.2-5.0) is "0.2", not "0.20000000000000018". But sometimes you need all the precision you can get, so repr(f)—that is, the machine-readable rendition—is not truncated, so repr(5.2-5.0) is "0.20000000000000018".
Now the only thing left to understand is what the interpreter shell does. As Ashwini Chaudhary explains, just evaluating something in the shell prints out its repr, while the print statement prints out its str.
shell uses repr():
In [1]: print repr(5.2-5.0)
0.20000000000000018
In [2]: print str(5.2-5.0)
0.2
In [3]: print 5.2-5.0
0.2
The default implementation of float.__str__ limits the output to 12 digits only.
Thus, the least significant digits are dropped and what is left is the value 0.2.
To print more digits (if available), use string formatting:
print '%f' % result # prints 0.200000
That defaults to 6 digits, but you can specify more precision:
print '%.16f' % result # prints 0.2000000000000002
Alternatively, python offers a newer string formatting method too:
print '{0:.16f}'.format(result) # prints 0.2000000000000002
Why python produces the 'imprecise' result in the first place has everything to do with the imprecise nature of floating point arithmetic. Use the decimal module instead if you need more predictable precision:
>>> from decimal import *
>>> getcontext().prec = 1
>>> Decimal(5.2) - Decimal(5.0)
Decimal('0.2')
Python has two different ways of converting an object to a string, the __str__ and __repr__ methods. __str__ is meant to be a normal string output and is used by print; __repr__ is meant to be a more exact representation and is what is displayed when you don't use print, or when you print the contents of a list or dictionary. __str__ rounds floating-point values.
As for why the actual result of the subtraction is 0.20000000000000018 rather than 0.2 exactly, it has to do with the internal representation of floating point. It's impossible to represent 5.2 exactly because it's an infinitely repeating binary number. The closest that you can come is approximately 5.20000000000000018.

Python not showing decimals

Python is exhibiting a strange behaviour that I didnt witness before, not sure what I did or what happened but basically it doesnt operate in shell with decimals
if i type simple
>>> 2/3
0
>>> 3/2
1
if i try to format that through % or format() it doesnt do much either, basically it just doesnt recognize any decimal
>>> a =2/3
>>> a
0
>>> format(a, '.5f')
'0.00000'
I needed a simple division for my code to check something and all of a sudden I encountered something as bizzare as this
I use Python 2.7
In Python 2, / performs "integer division" by default. If you put
from __future__ import division
at the top of your script, it will do the division you want, which will be the default behavior in Python 3. Alternatively, if you want to stay compatible with old Python versions (not recommended for new code), do
2. / 3.
or, with variables
x / float(y)
a = 2/3.
or
a = 2./3
At least one number needs to be a float!
You are performing operations exclusively on integers, which means fractional components of numbers are dropped. You need something like 2.0/3 instead, so floating point arithmetic will be used.

Categories