The following simple code:
from decimal import getcontext
from decimal import *
import math
context = getcontext()
context.prec = 300
def f(x):
return Decimal(math.atan(10**(-x+1)))
def xNext(x,y):
return x-y*f(2)
def yNext(x,y):
return y+x*f(2)
x= Decimal(1)
y = Decimal(0)
x=xNext(x,y)
y=yNext(x,y)
x=xNext(x,y)
y=yNext(x,y)
x=xNext(x,y)
y=yNext(x,y)
print("{:.16f}".format(x))
print("{:.16f}".format(y))
returns
0.9702971603146833
0.2950554229911823
Which is wrong, should be around 0.97019857 and 0.2980158649
I thought this was a rounding error but this code should be working to 300 decimal places.
Not sure if different problem or not really going to 300 places...
EDIT: Yeah, I doubt it's a rounding error, I've just done the same process on wolfram only to around 20 decimal places at a time and my answer's more accurate than this one.
Decimal doesn't extend your precision, because you use the math module. But that's not the point. Are you sure you calculation is correct? Just tried:
x, y = 1, 0
x, y = xNext(x,y), yNext(x,y)
x, y = xNext(x,y), yNext(x,y)
x, y = xNext(x,y), yNext(x,y)
And it leads to
0.970198479132
0.298015864998
which is basically your expected result.
I think that the problem lies here :
return Decimal(math.atan(10**(-x+1)))
I would imagine that ALL of the calculations in that formula (especially the math.atan function) will be calculated as a normal precision floating point number - and then converted back to a 300 decimal point Decimal.
If you want 300 point precision, you MUST find a way to ensure that every calculation is executed to that level of precision or better, as your result will only be as precise as your LEAST precise calculation.
Related
From the documentation page of Decimal, I thought that once we use decimal to compute, it'll be a correct result without any floating error.
But when I try this equation
from decimal import Decimal, getcontext
getcontext().prec = 250
a = Decimal('6')
b = Decimal('500000')
b = a ** b
print('prec: ' + str(getcontext().prec) + ', ', end='')
print(b.ln() / a.ln())
It gives me different result!
I want to calculate the digit of 6**500000 in base-6 representation, so my expect result would be int(b.ln() / a.ln()) + 1, which should be 500001. However, when I set the prec to 250, it gives me the wrong result. How can I solve this?
Also, if I want to output the result without the scientific notation (i.e. 5E+5), what should I do?
The Documentation clearly show the parameters of getcontext()
when you simply execute getcontext() it can show its built-in parameters.
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
When you can change getcontext().prec = 250 then it can only overwrite prec value.
the main thing which can effect the result is rounding parameter right after the prec, It is built-in parameter rounding=ROUND_HALF_EVEN. I want to change this to None but it can show an error.
so it can clarify that no matter what we can do it must change even slightly because of rounding parameter.
Note: your result may also effect because of other built-in parameters.
If you're after very precise or symbolic math and/or being bitten by IEEE 754 float aliasing, check out SymPy
>>> from sympy import log, N
>>> expr = log(6**50000) / log(6)
>>> N(expr)
50000.0000000000
The documentation for Decimal.ln() states:
Return the natural (base e) logarithm of the operand. The result is correctly rounded using the ROUND_HALF_EVEN rounding mode.
When you changed the precision, more digits of the numbers were calculated and they are then rounded down instead of up. The number needs to be rounded because it is not necessarily within the specified precision, or even rational at all.
For outputting results in scientific notation, see this question.
I was investigating different rounding method using Python built-in solution and some other external libraries such SymPy and while doing so I stumbled upon some cases that I need help with understanding the reason behind it.
Ex-1:
print(round(1.0065,3))
output:
1.006
In the first case, using the Python built-in rounding function the output was 1.006 instead of 1.007 and I can understand that this is not a mistake as Python rounds to the nearest even and that's known as Bankers rounding.
And this is why I from the beginning started searching for another way to control the rounding behaviour. With a quick search, I've found decimal.Decimal module which can easily handle decimal values and efficiently round is using quantize() as in this example:
from decimal import Decimal, getcontext, ROUND_HALF_UP
context= getcontext()
context.rounding='ROUND_HALF_UP'
print(Decimal('1.0065').quantize(Decimal('.001')))
output:1.007
This is a very good solution but the only problem is it is not easy to be hardcoded in long math expressions as I'll need to convert every number to string then after using decimal I will pass it the precession as in the form of "0.001" instead of writing '3' directly as in the case of built-in round.
While searching for another solution I found that SymPy, which I already use a lot in my scripts, offers some very powerful functions that might help but when I tried it the output was not as I expected.
Ex-1 using SymPy sympify():
print(sympify(1.0065).evalf(3))
output: 1.01
Ex-2 using SymPy N (normalize):
print(N(1.0065,3))
output: 1.01
Af first the output was a little bit weird but after investigating I realized that N and sympify already performing round right but rounding to significant figures, not to decimal places.
And here the questions come:
As I can use with Decimal objects getcontext().rounding='ROUND_HALF_UP' to change the rounding behaviour, is there a way to change the N and sympify rounding behaviour to decimal places instead of significant figures?
Instead of re-implementing decimal rounding in SymPy, perhaps use decimal to do the rounding, but hide the calculation in a utility function:
import sympy as sym
import decimal
from decimal import Decimal as D
def dround(d, ndigits, rounding=decimal.ROUND_HALF_UP):
result = D(str(d)).quantize(D('0.1')**ndigits, rounding=rounding)
# result = sym.sympify(result) # if you want a SymPy Float
return result
for x in [0.0065, 1.0065, 10.0065, 100.0065]:
print(dround(x, 3))
prints
0.007
1.007
10.007
100.007
The n of evalf gives the first n significant digits of x (measured from the left). If you use x.round(3) it will round x to the nth digit from the decimal point and can be positive (right of decimal pt) or negative (left of decimal pt).
>>> for x in '0.0065, 1.0065, 10.0065, 100.0065'.split(', '):
... print S(x).round(3)
0.006
1.006
10.007
100.007
>>> int(S(12345).round(-2))
12300
First of all, N and evalf are essentially the same thing; N(x, n) amounts to sympify(x).evalf(n). In your case, since x is a Python float, it's easier to use N because it sympifies the input.
To get three digits after decimal dot, use N(x, 3 + log(x, 10) + 1). The adjustment log(x, 10) + 1 is 0 when x is between 0.1 and 1; in this case the number of significant digits is the same as the number of digits after the decimal dot. If x is larger, we get more significant digits.
Example:
for x in [0.0065, 1.0065, 10.0065, 100.0065]:
print(N(x, 3 + log(x, 10) + 1))
prints
0.006
1.007
10.007
100.007
The transition from 6 to 7 is curious, but not entirely surprising. These numbers are not exactly represented in binary system, so the truncation to nearest double-precision float may be a factor here. I've made a few additional observation on this effect on my blog.
Question is pretty self-explanatory. I've seen a couple of examples for pi but not for trigo functions. Maybe one could use a Taylor series as done here but I'm not entirely sure how to implement that in python. Especially how to store so many digits.
I should mention: this ideally would run on vanilla python i.e. no numpy etc.
Thanks!
Edit: as said, I know the question has been asked before but it's in java and I was looking for a python implementation :)
Edit 2: wow I wasn't aware people here can be so self-absorbed. I did try several approaches but none would work. I thought this a place you can ask for advice, guess I was wrong
last edit: For anyone who might find this useful: many angles can be calculated as a multiple of sqrt(2), sqrt(3) and Phi (1.61803..) Since those numbers are widely available with a precision up to 10mio digits, it's useful to have them in a file and read them in your program directly
mpmath is the way:
from mpmath import mp
precision = 1000000
mp.dps = precision
mp.cos(0.1)
If unable to install mpmath or any other module you could try polynomial approximation as suggested.
where Rn is the Lagrange Remainder
Note that Rn grows fast as soon as x moves away from the center x0, so be careful using Maclaurin series (Taylor series centered in 0) when trying to calculate sin(x) or cos(x) of arbitrary x.
Try this
import math
from decimal import *
def sin_taylor(x, decimals):
p = 0
getcontext().prec = decimals
for n in range(decimals):
p += Decimal(((-1)**n)*(x**(2*n+1)))/(Decimal(math.factorial(2*n+1)))
return p
def cos_taylor(x, decimals):
p = 0
getcontext().prec = decimals
for n in range(decimals):
p += Decimal(((-1)**n)*(x**(2*n)))/(Decimal(math.factorial(2*n)))
return p
if __name__ == "__main__":
ang = 0.1
decimals = 1000000
print('sin:', sin_taylor(ang, decimals))
print('cos:', cos_taylor(ang, decimals))
import math
x = .5
def sin(x):
sum = 0
for a in range(0,50): #this number (50) to be changed for more accurate results
sum+=(math.pow(-1,a))/(math.factorial(2*a+1))*(math.pow(x,2*a+1))
return sum
ans = sin(x)
print('{0:.15f}'.format(ans)) #change the 15 for more decimal places
Here is an example of implementing the Taylor series using python as you suggested above. Changing to cos wouldn't be too hard after that.
EDIT:
Added in the formatting of the last line in order to actual print out more decimal places.
What I wanted to do:
Paradox: Suppose Peter Parker were running to catch a bus. To reach it, he’d first need to get halfway there. Before that, he’d need to get a quarter of the way there……before a quarter, an eighth; before an eighth, a 16th; and so on. Since the distance can be halved infinitely, he’d be trying to complete an infinite number of tasks… WHICH WOULD BE LOGICALLY IMPOSSIBLE!
I tried to to resolve this paradox using Python
I have some questions:
How can I get a number to have no limitations with decimals? Python limits the numbers of decimals, I think to 12, How to make that number infinite?
Aparrently there is no way to make the float decimals infinite, the closest I could get was using this
from decimal import Decimal
Is this the correct way of asking the user for an input in numbers?
Code modified
from decimal import Decimal
def infinite_loop():
x = 0;
number = Decimal(raw_input())
while x != number:
x = x + number
number = number/2
print x
infinite_loop()
What you ask is impossible. There are no "infinite precision" floating point values in the real world of finite computing systems. If there were, a single floating point value could consume all of the system's resources. pi * d? Ooops!! pi is infinite. There goes the system!
What you can do, however, is get arbitrary precision decimal values. They're still finite, but you can choose how much precision you want (and are willing to pay for). E.g.:
>>> from decimal import Decimal
>>> x = Decimal('1.' + '0' * 200)
>>> x
Decimal('1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
Now you have 200 digits of precision. Not enough? Go 400. 800. However many you like. As long as that's a finite, practical value.
If you want "infinite" precision (e.g. a decimal number that be extended as far as you have memory for), either use Python's builtin module Decimal or for more heavy computation, mpmath:
import mpmath as mp
mp.mp.dps = 100
print mp.sqrt(mp.mpf(2))
>> 1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573
The reason I'm asking this is because there is a validation in OpenERP that it's driving me crazy:
>>> round(1.2 / 0.01) * 0.01
1.2
>>> round(12.2 / 0.01) * 0.01
12.200000000000001
>>> round(122.2 / 0.01) * 0.01
122.2
>>> round(1222.2 / 0.01) * 0.01
1222.2
As you can see, the second round is returning an odd value.
Can someone explain to me why is this happening?
This has in fact nothing to with round, you can witness the exact same problem if you just do 1220 * 0.01:
>>> 1220*0.01
12.200000000000001
What you see here is a standard floating point issue.
You might want to read what Wikipedia has to say about floating point accuracy problems:
The fact that floating-point numbers cannot precisely represent all real numbers, and that floating-point operations cannot precisely represent true arithmetic operations, leads to many surprising situations. This is related to the finite precision with which computers generally represent numbers.
Also see:
Numerical analysis
Numerical stability
A simple example for numerical instability with floating-point:
the numbers are finite. lets say we save 4 digits after the dot in a given computer or language.
0.0001 multiplied with 0.0001 would result something lower than 0.0001, and therefore it is impossible to save this result!
In this case if you calculate (0.0001 x 0.0001) / 0.0001 = 0.0001, this simple computer will fail in being accurate because it tries to multiply first and only afterwards to divide. In javascript, dividing with fractions leads to similar inaccuracies.
The float type that you are using stores binary floating point numbers. Not every decimal number is exactly representable as a float. In particular there is no exact representation of 1.2 or 0.01, so the actual number stored in the computer will differ very slightly from the value written in the source code. This representation error can cause calculations to give slightly different results from the exact mathematical result.
It is important to be aware of the possibility of small errors whenever you use floating point arithmetic, and write your code to work well even when the values calculated are not exactly correct. For example, you should consider rounding values to a certain number of decimal places when displaying them to the user.
You could also consider using the decimal type which stores decimal floating point numbers. If you use decimal then 1.2 can be stored exactly. However, working with decimal will reduce the performance of your code. You should only use it if exact representation of decimal numbers is important. You should also be aware that decimal does not mean that you'll never have any problems. For example 0.33333... has no exact representation as a decimal.
There is a loss of accuracy from the division due to the way floating point numbers are stored, so you see that this identity doesn't hold
>>> 12.2 / 0.01 * 0.01 == 12.2
False
bArmageddon, has provided a bunch of links which you should read, but I believe the takeaway message is don't expect floats to give exact results unless you fully understand the limits of the representation.
Especially don't use floats to represent amounts of money! which is a pretty common mistake
Python also has the decimal module, which may be useful to you
Others have answered your question and mentioned that many numbers don't have an exact binary fractional representation. If you are accustomed to working only with decimal numbers, it can seem deeply weird that a nice, "round" number like 0.01 could be a non-terminating number in some other base. In the spirit of "seeing is believing," here's a little Python program that will print out a binary representation of any number to any desired number of digits.
from decimal import Decimal
n = Decimal("0.01") # the number to print the binary equivalent of
m = 1000 # maximum number of digits to print
p = -1
r = []
w = int(n)
n = abs(n) - abs(w)
while n and -p < m:
s = Decimal(2) ** p
if n >= s:
r.append("1")
n -= s
else:
r.append("0")
p -= 1
print "%s.%s%s" % ("-" if w < 0 else "", bin(abs(w))[2:],
"".join(r), "..." if n else "")