How Python calculate number? [duplicate] - python

This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
python - decimal place issues with floats
In [4]: 52+121.2
Out[4]: 173.19999999999999

Short answer: Python uses binary arithmetic for floating-point numbers, not decimal arithmetic. Decimal fractions are not exactly representable in binary.
Long answer: What Every Computer Scientist Should Know About Floating-Point Arithmetic

If you're familiar with the idea that the number "thirteen point two" is written in base ten as "13.2" because it's "10^1 * 1 + 10^0 * 3 + 10^-1 * 2" then try to do the same thing with a base of 2 instead of 10 for the number 173.2.
Here's the whole part:
(1 * 2^7) + (0 * 2^6) + (1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (1 * 2^2) + (0 * 2^1) + (0 * 2^0)
Now here's the start fractional part:
(0 * 2^-1) + (0 * 2^-2) + (1 * 2^-3)
That's .125, which isn't yet 2/10ths so we need more additions that are of the form (1 * 2^-n), we can carry this out a bit further with (1 * 2^-4) + (1 * 2^-7), which gets us a bit closer ... to 0.1953125, but no matter how long we do this, we'll never get to ".2" because ".2" is not representable as a addition of sums of numbers of the form (1 * 2^-n).
Also see .9999… = 1.0 (http://en.wikipedia.org/wiki/0.999...)

Try this:
>>> from decimal import Decimal
>>> Decimal("52") + Decimal("121.2")
Decimal("173.2")

The other answers, pointing to good floating-point resources, are where to start. If you understand floating point roundoff errors, however, and just want your numbers to look prettier and not include a dozen extra digits for that extra last bit of precision, try using str() to print the number rather than repr():
>>> 52+121.2
173.19999999999999
>>> str(52+121.2)
'173.2'

Related

Python float operations give complex result

eq = (((1 - (2 * normal_rpm - s8) ** s1) * s2 * math.sin(normal_rpm ** s3 / s4) * (1 - math.sin((normal_rpm + s5) ** (2) + 5) + s6) / (s7))) + 0.67
is my formula for this variable, where the S variables are floats. this sometimes returns a result like this
(0.6806708980989302+0.008606807113252896j)
I cannot use this result in further math, I need a float, even if I have to round the answer a bit.
This is not a rounding problem, but you are raising a negative number to a fractional exponent (e.g. you're taking the square root of -5).
For example:
In [2]: (-5)**0.5
Out[2]: (1.3691967456605067e-16+2.23606797749979j)
If you cannot accept complex numbers as result then the only other logical way out is to raise an error when this happens (there is no real number that multiplied by itself gives, or gets near, -5).
If this is not expected you should double-check the formula or formulas preceding it because may be there is a typo, or may be there are some preconditions you need to check before applying this formula.

Python-properly adding fractions [duplicate]

This question already has answers here:
adding two fractions in python
(3 answers)
Closed 4 years ago.
I would like to do some calculation with fractions, keeping all the numbers as fractions and never converting to decimals.
For example 1/2+1/4=3/4. We can do this in Python for example by using a function from the fractions class: Fraction(1/2+1/4) or Fraction(1/2)+Fraction(1/4), if we want.
However I can't get Python to give me the correct fraction for 1/2+1/3 because 1/3 is a non-terminating decimal. Fraction(1/2+1/3) doesn't work, nor does Fraction(Fraction(1/2)+Fraction(1/3)).
***EDIT: The reason why I used Fraction(1/2) instead of Fraction(1,2) is because, in my code I know that I'm working only with fractions but I won't know the numerator and denominator of a fraction just the fraction itself.
Sure you can.
>> from fractions import Fraction
>> Fraction(1, 2) + Fraction(1, 3)
Fraction(5, 6)
Your notation seems to be the problem. Notice that:
>>> Fraction(1/3) == Fraction(1, 3)
False
while
>>> Fraction(1/2) == Fraction(1, 2)
True
As a result:
>>> Fraction(1/2) + Fraction(1/3)
Fraction(15011998757901653, 18014398509481984) # almost 5/6 but not quite
That happens because when you type Fraction(1/3) the 1/3 is calculated first and then passed to Fraction. On the contrary, Fraction(1, 3) simply creates a fraction using 1 as the numerator and 3 as the denominator.
You can use the Fraction function in this manner.
from fractions import Fraction
f = Fraction(1,2) + Fraction(1,3)
print (f)
Output: Fraction(5,6)

Calculating large fractions in Python?

I am trying to calculate fractions in Python 2.7. The limit_denominator method works great for the first 15 iterations of this code. However, then the code gets stuck in a loop, outputting denominators less than 1,000,000
Fraction = 1217471/860882
When I don't use limit_denominator, I get repeat outputs like this:
Fraction = 141421356237/100000000000
Eventually I want to iterate i to 1000, so my fractions will be very large. Any help?
from fractions import *
i = 0
x = 1/2.0
x1 = 0
count = 0
while i < 20:
(y) = (1.0 + (x))
(x) = (1 / (2.0 + (x)))
y1 = Fraction(str(y)).limit_denominator()
print("\nFraction = " + str(y1))
i += 1
The values converge to sqrt(2.0), which gives you a narrow range of fractions that will accurately represent the 64-bit float value. Your rational fraction cannot be more accurate than the float you give it.
If you want larger denominators, then you have to specify a larger denominator limit. You're still limited by the float accuracy: once you converge within the accuracy of your computational type (likely float64), you will not get more accuracy in your rational representation thereof. If you want greater accuracy, convert the entirety to fraction computations:
from fractions import *
x = Fraction(1,2)
for i in range(40):
y = Fraction(1) + x
x = Fraction(1) / (Fraction(2) + x)
print("Fraction = " + str(y))
Output:
Fraction = 3/2
Fraction = 7/5
Fraction = 17/12
Fraction = 41/29
Fraction = 99/70
Fraction = 239/169
Fraction = 577/408
Fraction = 1393/985
Fraction = 3363/2378
Fraction = 8119/5741
Fraction = 19601/13860
Fraction = 47321/33461
Fraction = 114243/80782
Fraction = 275807/195025
Fraction = 665857/470832
Fraction = 1607521/1136689
Fraction = 3880899/2744210
Fraction = 9369319/6625109
Fraction = 22619537/15994428
Fraction = 54608393/38613965
Fraction = 131836323/93222358
Fraction = 318281039/225058681
Fraction = 768398401/543339720
Fraction = 1855077841/1311738121
Fraction = 4478554083/3166815962
Fraction = 10812186007/7645370045
Fraction = 26102926097/18457556052
Fraction = 63018038201/44560482149
Fraction = 152139002499/107578520350
Fraction = 367296043199/259717522849
Fraction = 886731088897/627013566048
Fraction = 2140758220993/1513744654945
Fraction = 5168247530883/3654502875938
Fraction = 12477253282759/8822750406821
Fraction = 30122754096401/21300003689580
Fraction = 72722761475561/51422757785981
Fraction = 175568277047523/124145519261542
Fraction = 423859315570607/299713796309065
Fraction = 1023286908188737/723573111879672
Fraction = 2470433131948081/1746860020068409
I rewrote your code trying to solve your problem because i did not understand the need for limit_denominator. This is the result:
from fractions import *
x = Fraction(1, 2)
for i in range(1000):
y = 1 + Fraction(x)
print 'Y', y
x = 1 / (2 + x)
print 'X', x
The problem is that computers don't really understand numbers, instead they work with an abstract representation of numbers in memory called floating point (the origin of float i assume). This representation has a given precision (limit) which depends on the amount of memory reserved for the data type. That is why int32 has fewer accepted values than int64 for example.
However, python has a smart and efficient way of calculating large numbers.
Besides, the fractions library provides you with a way of representing numbers (fractions) that escape (not really, after all it is a computer) the floating point numbers constraint.
If you want to dive more into floating point arithmetic I recommend the all-mighty Numerical Analysis by Burden & Faires and Numerical Methods by Dr David Ham.
As Prune says, it's best to avoid floats when working with Fraction. And if you want to convert your fraction to a decimal without losing any accuracy you need to use a numeric type like Decimal which has enough precision. Another option is to just work with Python integers, and scale up your numerator with a sufficiently large multiplier.
Your series finds the convergents to the continued fraction of the square root of two. If you want to loop over all the convergents you can use the algorithm shown in Prune's answer. But if you want to calculate sqrt(2) quickly to a large number of digits, there's a better way, known as Hero's method (or Heron's method). This is a special case of Newton's method for calculating roots of algebraic equations. Instead of calculating the terms for each i in Prune's algorithm 1 by 1 we're essentially doubling i on each iteration, so the numerator & denominator grow large very quickly, doubling the accuracy of the answer on each loop iteration.
Here's a short demo that calculates sqrt(2) accurate to 100 digits. I'd normally do this using plain Python integers (or long integers in Python 2), but it's also easy to do it with a Fraction.
from __future__ import print_function
from fractions import Fraction as F
digits = 100
m = 10 ** digits
x = F(1, 1)
while x.denominator < m:
print(x)
x = x / 2 + 1 / x
print()
print(m * x.numerator // x.denominator)
output
1
3/2
17/12
577/408
665857/470832
886731088897/627013566048
1572584048032918633353217/1111984844349868137938112
4946041176255201878775086487573351061418968498177/3497379255757941172020851852070562919437964212608
48926646634423881954586808839856694558492182258668537145547700898547222910968507268117381704646657/34596363615919099765318545389014861517389860071988342648187104766246565694525469768325292176831232
14142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727
Tested on Python 2.6 and 3.6

Measuring the accuracy of floating point results to N decimal places

I'm testing some implementations of Pi in python (64-bit OS) and am interested in measuring how accurate the answer is (how many decimal places were correct?) for increasing iterations. I don't wish to compare more than 15 decimal places because beyond that the floating point representation itself is inaccurate.
E.g. for a low iteration count, the answer I got is
>>> x
3.140638056205993
I wish to compare to math.pi
>>> math.pi
3.141592653589793
For the above I wish my answer to be 3 (3rd decimal is wrong)
The way I've done it is:
>>> p = str('%.51f' % math.pi)
>>> q = str('%.51f' % x)
>>> for i,(a,b) in enumerate(zip(p,q)):
... if a != b:
... break
The above looks clumsy to me, i.e. converting floats to strings and then comparing character by character, is there a better way of doing this, say more Pythonic or that uses the raw float values themselves?
Btw I found math.frexp, can this be used to do this?
>>> math.frexp(x)
(0.7851595140514982, 2)
You can compute the logarithm of the difference between the two
>>> val = 3.140638056205993
>>> epsilon = abs(val - math.pi)
>>> abs(int(math.log(epsilon, 10))) + 1
3
Essentially, you're finding out which power of 10 does it take to equal the difference between the two numbers. This only works if the difference between the two numbers is less than 1.

Accurate roots of negative numbers in Python

I'm looking for a way to accurately compute roots of negative numbers in Python. I believe that the inaccuracies I see are related to floating point, but it seems like there should be a way to get the right answer for something as simple as this:
>>(-1+0j)**0.1
(0.9510565162951535+0.3090169943749474j)
The answer I expect in this case is (0+1j). Though raising Python's response to the tenth does come close to -1, I am looking for an answer obtained from a more precise method, i.e., one that when raised to the tenth equals exactly -1, not a number really close to -1.
Is there a way to do this correctly with either a native Python library, or sympy/numpy/scipy etc?
-1 has not one but 10 complex tenth roots. You got only one of them. If a is your returned root, a ** 5 is also a root:
(a ** 5) ** 10 = (a ** 10) ** 5 = (-1) ** 5 = -1
But if you run:
a = (-1 + 0j) ** 0.1
print(a)
print(a ** 5)
you'll get:
(0.951056516295+0.309016994375j)
(1.11022302463e-16+1j)
You see a ** 5 is very close to 1j.

Categories