How to find Log10(x) without Math library [duplicate] - python

I have tried to create a program to calculate the base-10 logarithm based on the Taylor series-based algorithm described in "The Mathematical-Function Computation Handbook" (I found an online copy via my University's library).
A similar algorithm is given on another question on StackOverflow for which I cannot find the link right now.
10.3.2 Computing logarithms in a decimal base
For a decimal base, the base-10 logarithm is the natural choice, and the decomposition of the argument into an
exponent and a fraction gives us a decimal representation:
x = (−1)^s × f × 10^n, either f = 0 exactly, or f is in [1/10, 1).
If f ≤√1/10, set f = 10 × f and n = n − 1, so that f is now in the interval (√1/10,√10]. Then introduce a change of variable, a Taylor-series expansion, and a polynomial representation of that expansion:
z = (f − 1)/( f + 1),
f = (1 + z)/(1 − z),
D = 2 log10(e)
= 2/ log(10)
log10( f) = D × (z + z3/3 + z5/5 + z7/7 + z9/9 + z11/11 + · · · )
≈ D × z + z3Q(z2), polynomial fit incorporates D in Q(z2).
For f in (√1/10,√10], we have z in roughly [−0.5195,+0.5195]. The wider range of z requires longer polynomials compared to the binary case, and also makes the correction term z3Q(z2) relatively larger. Its magnitude does not exceed |0.35z|, so it provides barely one extra decimal digit of precision, instead of two. Accurate computation of z is easier than in the binary case: just set z = fl(fl(f−12)−12)/fl(f+1).
For this I wrote this program in Python:
def log10(x):
n = 0.0 #Start exponent of base 10
while (x >= 1.0):
x = x/10.0
n+=1
# if x <= sqrt(1/10)
if(x<=0.316227766016838):
x = x*10.0
n = n-1
#Produce a change of variable
z = (x-1.0)/(x+1.0)
D = 4.60517018598809 #2*log10(e)
sum = z
for k in range(3,111,2):
sum+=(z**k)/k
return D*n*sum
I compared the results to the math.log10 function, and the results are not as expected. My biggest issue when debugging is understanding the algorithm and why it works.

Here is my source code after the suggested corrections (changed return statement to D*sum+n fixed the value of D, and changed if(x<=0.316227766016838) to while(x<=0.316227766016838). I added some if statements to handle exceptional cases.
The code below works well within my target precision of 6 digits (I tested it with very small input, large input).
def log10(x):
# Handle exceptional cases
if (x == 1):
return 0
if (x == 0):
return float('-Inf')
if (x < 0):
return float('nan')
n = 0 #Start exponent of base 10
while (x >= 1.0):
x = x/10.0
n+=1
# if x <= sqrt(1/10)
while(x<=0.316227766016838):
x = x*10.0
n = n-1
#Produce a change of variable
z = (x-1.0)/(x+1.0)
D = 0.868588964 #2*log10(e)
#Taylor series
sum = z
for k in range(3,23,2):
sum+=(z**k)/k
return D*sum+n

Related

Error for trapezoidal method increases with N if integral equals 0 [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed last month.
I've written a simple implementation of the trapezoidal method to find the integral of sine:
def trapezoidal_method(a: float, b: float, n: int) -> float:
length = (b - a)/n
integral = 0
start = a
integral += math.sin(a)/2
for _ in range(1, n):
integral += math.sin(start + length)
start += length
integral += math.sin(b)/2
return integral * length
It converges as expected for most situations...
but it goes crazy when the result should be 0 (like integrating from -1 to 1):
How do I fix this? Tried explicitly casting n to float and tried using the decimal library, didn't change anything
That's (mainly) due to floating number precision. In the case of functions as sine, which are transcendental by nature, only an approximation of it is possible (or depends on its implementation).
By adding a (very quick!) a print of the x and y values at each term of the sequence you will see the odd symmetry is not respected... or at least with a small error. So, f(x) = -f(-x) + error.
import math
def trapezoidal_method(func, a: float, b: float, n: int) -> float:
length = (b - a)/n
integral = 0
start = a
integral += func(a)/2
for _ in range(1, n):
integral += func(start + length)
start += length
integral += func(b)/2
# evaluation for each term of the sequence
print(0, f'x={a} y={func(a)}')
x = a
for i in range(1, n):
x += length
y = func(x)
print(i, f'{x=} {y=}')
print(n, f'x={b} y={func(b)}')
return integral * length
A test with sin
a, b, n = -1, 1, 7
func = math.sin
tm = trapezoidal_method(func, a, b, n)
Output
0 x=-1 y=-0.8414709848078965
1 x=-0.7142857142857143 y=-0.6550778971785186
2 x=-0.4285714285714286 y=-0.41557185499305205 # <-
3 x=-0.1428571428571429 y=-0.1423717297922637
4 x= 0.1428571428571428 y=0.1423717297922636
5 x= 0.4285714285714285 y=0.41557185499305194 # <-
6 x= 0.7142857142857142 y=0.6550778971785185
8 x= 1 y=0.8414709848078965
-1.586032892321652e-16
Notice that also the x-values maybe slightly different!
By choosing suitable limits of integration and amount of strips then the in some cases the integral maybe 0. Here an example with the diagonal function
a, b, n = -1, 1, 6
func = lambda x: x
tm = trapezoidal_method(func, a, b, n)
Output
0 x=-3.0 y=-3.0
1 x=-2.0 y=-2.0
2 x=-1.0 y=-1.0
3 x=0.0 y=0.0
4 x=1.0 y=1.0
5 x=2.0 y=2.0
7 x=3.0 y=3.0
0.0

Algorithm for computing base-10 logarithm in Python

I have tried to create a program to calculate the base-10 logarithm based on the Taylor series-based algorithm described in "The Mathematical-Function Computation Handbook" (I found an online copy via my University's library).
A similar algorithm is given on another question on StackOverflow for which I cannot find the link right now.
10.3.2 Computing logarithms in a decimal base
For a decimal base, the base-10 logarithm is the natural choice, and the decomposition of the argument into an
exponent and a fraction gives us a decimal representation:
x = (−1)^s × f × 10^n, either f = 0 exactly, or f is in [1/10, 1).
If f ≤√1/10, set f = 10 × f and n = n − 1, so that f is now in the interval (√1/10,√10]. Then introduce a change of variable, a Taylor-series expansion, and a polynomial representation of that expansion:
z = (f − 1)/( f + 1),
f = (1 + z)/(1 − z),
D = 2 log10(e)
= 2/ log(10)
log10( f) = D × (z + z3/3 + z5/5 + z7/7 + z9/9 + z11/11 + · · · )
≈ D × z + z3Q(z2), polynomial fit incorporates D in Q(z2).
For f in (√1/10,√10], we have z in roughly [−0.5195,+0.5195]. The wider range of z requires longer polynomials compared to the binary case, and also makes the correction term z3Q(z2) relatively larger. Its magnitude does not exceed |0.35z|, so it provides barely one extra decimal digit of precision, instead of two. Accurate computation of z is easier than in the binary case: just set z = fl(fl(f−12)−12)/fl(f+1).
For this I wrote this program in Python:
def log10(x):
n = 0.0 #Start exponent of base 10
while (x >= 1.0):
x = x/10.0
n+=1
# if x <= sqrt(1/10)
if(x<=0.316227766016838):
x = x*10.0
n = n-1
#Produce a change of variable
z = (x-1.0)/(x+1.0)
D = 4.60517018598809 #2*log10(e)
sum = z
for k in range(3,111,2):
sum+=(z**k)/k
return D*n*sum
I compared the results to the math.log10 function, and the results are not as expected. My biggest issue when debugging is understanding the algorithm and why it works.
Here is my source code after the suggested corrections (changed return statement to D*sum+n fixed the value of D, and changed if(x<=0.316227766016838) to while(x<=0.316227766016838). I added some if statements to handle exceptional cases.
The code below works well within my target precision of 6 digits (I tested it with very small input, large input).
def log10(x):
# Handle exceptional cases
if (x == 1):
return 0
if (x == 0):
return float('-Inf')
if (x < 0):
return float('nan')
n = 0 #Start exponent of base 10
while (x >= 1.0):
x = x/10.0
n+=1
# if x <= sqrt(1/10)
while(x<=0.316227766016838):
x = x*10.0
n = n-1
#Produce a change of variable
z = (x-1.0)/(x+1.0)
D = 0.868588964 #2*log10(e)
#Taylor series
sum = z
for k in range(3,23,2):
sum+=(z**k)/k
return D*sum+n

Python 3 - float(X) * i = int(Z)

I have a very large number, both before and after the decimal, but for this I'll just call it 4.58.
I want to know the number, Y, that will yield me an integer if multiplied by X and not any sort of float number.
Here is my code:
from decimal import *
setcontext(ExtendedContext)
getcontext().prec = 300
x=Decimal('4.58')
while True:
i=1
a=Decimal(i*x)
if float(a).is_integer():
print(i*x)
break
else:
i=+1
However, this method is incredibly slow and inefficient. I was wondering how could I implement continued fractions or some other method to make it predict the value of Y?
Edit
The decimal module stores float numbers more accurately (As strings), so 0.5 won't become 0.499999999.
Edit 2
I've got X (4.58).
I want to know what number will multiply by X to make an integer; as efficiently as possible.
Edit 3
Okay, maybe not my best question yet.
Here's my dilemma.
I've got a number spat out from a trivial programme I made. That number is a decimal number, 1.5.
All I want to do is find what integer will multiply by my decimal to yield another integer.
For 1.5, the best answer will be 2. (1.5*2=3) (float*int=int)
My while-loop above will do that, eventually, but I just wanted to know whether there was a better way to do this, such as continued fractions; and if there was, how could I implement it.
Edit 4
Here's my code thanks to user6794072. It's lengthy but functional.
from gmpy2 import mpz, isqrt
from fractions import Fraction
import operator
import functools
from decimal import *
setcontext(ExtendedContext)
getcontext().prec = 300
def factors(n):
n = mpz(n)
result = set()
result |= {mpz(1), n}
def all_multiples(result, n, factor):
z = n
f = mpz(factor)
while z % f == 0:
result |= {f, z // f}
f += factor
return result
result = all_multiples(result, n, 2)
result = all_multiples(result, n, 3)
for i in range(1, isqrt(n) + 1, 6):
i1 = i + 1
i2 = i + 5
if not n % i1:
result |= {mpz(i1), n // i1}
if not n % i2:
result |= {mpz(i2), n // i2}
return result
j=Decimal('4.58')
a=(Fraction(j).numerator)
b=(Fraction(j).denominator)
y=(factors(a))
x=(factors(b))
q=([item for item in x if item not in y])
w=([item for item in y if item not in x]) q.extend(w)
p=(functools.reduce(operator.mul, q, 1)) ans=(p*j)
print(ans)
If I understand your question correctly, you want to find the smallest integer (i) that can be multiplied to a non-integer number (n) so that:
i*n is an integer
I would do this by finding the factors of the numerator and denominator for n. In your example, if n = 4.58, then you can extract 458 for the numerator and 100 for the denominator.
The multiples of 458 are 2 and 229
The multiples of 100 are 2, 2, 5, 5
You can cross off one instance of 2 for the numerator and denominator. Then your solution is just multiplying the remaining factors in the denominator: in this case, 2*5*5 or 50.
Well think of what if you wanted to reach z = 1 and then use the fact that z == z * 1 to scale the answer. For any float x != 0.0, y = 1/x will yield z = 1, so for arbitrary integer z, just use y = z/x.
I'm not a Python programmer, but what about round function?

Calculate e in Python [duplicate]

This question already has answers here:
Python basic math [closed]
(3 answers)
Closed 8 years ago.
What would the easiest way to represent the following equation be?
Just to clarify, my question is asking for some code that calculates the answer to the equation.
There are two problems with this:
The summation warrants an infinite loop which is impossible to get an answer from
I hoping for a long, detailed answer (maybe to 40 digits or so).
If you need more precision, you could try to use Fraction:
from fractions import Fraction # use rational numbers, they are more precise than floats
e = Fraction(0)
f = Fraction(1)
n = Fraction(1)
while True:
d = Fraction(1) / f # this ...
if d < Fraction(1, 10**40): # don't continue if the advancement is too small
break
e += d # ... and this are the formula you wrote for "e"
f *= n # calculate factorial incrementally, faster than calling "factorial()" all the time
n += Fraction(1) # we will use this for calculating the next factorial
print(float(e))
or Decimal:
from decimal import Decimal, getcontext
getcontext().prec = 40 # set the precision to 40 places
e = Decimal(0)
f = Decimal(1)
n = Decimal(1)
while True:
olde = e
e += Decimal(1) / f
if e == olde: # if there was no change in the 40 places, stop.
break
f *= n
n += Decimal(1)
print(float(e))
So here is e in 1000 places:
2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921817413596629043572900334295260595630738132328627943490763233829880753195251019011573834187930702154089149934884167509244761460668082264800168477411853742345442437107539077744992069551702761838606261331384583000752044933826560297606737113200709328709127443747047230696977209310141692836819025515108657463772111252389784425056953696770785449969967946864454905987931636889230098793127736178215424999229576351482208269895193668033182528869398496465105820939239829488793320362509443117301238197068416140397019837679320683282376464804295311802328782509819455815301756717361332069811250996181881593041690351598888519345807273866738589422879228499892086805825749279610484198444363463244968487560233624827041978623209002160990235304369941849146314093431738143640546253152096183690888707016768396424378140592714563549061303107208510383750510115747704171898610687396965521267154688957035044
To see more clearly what it does, here is its simplified version:
e = f = 1.0
for i in range(2, 16):
e += 1.0 / f
f *= i
print(e)
The obvious solution would be
import math
def e(n=10):
return sum(1 / float(math.factorial(i)) for i in range(n))
but it loses precision around n=20 (the error as compared to math.e is around 10^-16)
40-digits precision might be a challenge, and might require arbitrary precision arithmetic
I do not really see the point to have such precise "e" value since you won't be able to perform any calculations with such precision (if you do anything to it, you will lose that precision, unless you do everything in some arbitrary precision arithmetic).
To produce a similar result to my answer from a question on approximating pi:
from functools import wraps
def memoize(f):
"""Store and retrive previous results of the decorated function f."""
cache = {}
#wraps(f)
def func(*args):
if args not in cache:
cache[args] = f(*args)
return cache[args]
return func
#memoize
def fact(n):
"""Recursively calculates n!."""
if n <= 1:
return 1
return n * fact(n - 1)
def inverse_fact_n(start_n=0):
"""Generator producing the infinite series 1/n!."""
numerator = 1.0
denominator = start_n
while True:
yield numerator / fact(denominator)
denominator += 1
def approximate_e(steps=None, tolerance=None):
"""Calculate an approximation of e from summation of 1/n!."""
if steps is None and tolerance is None:
raise ValueError("Must supply one of steps or tolerance.")
series = inverse_fact_n()
if steps is not None: # stepwise method
return sum(next(series) for _ in range(steps))
output = 0 # tolerance method
term = next(series)
while abs(term) > tolerance:
output += term
term = next(series)
return output
if __name__ == "__main__":
from math import e
print("math.e:\t\t{0:.20f}.".format(e))
stepwise = approximate_e(steps=100)
print("Stepwise:\t{0:.20f}.".format(stepwise))
tolerated = approximate_e(tolerance=0.0000000001)
print("Tolerated:\t{0:.20f}.".format(tolerated))
The function approximate_e allows you to specify either:
A number of steps ("I want it to take this long"); or
A desired tolerance ("I want it to be this accurate").
There is some relatively advanced Python around this (e.g. the memoizing decorator function and the generator function to produce the series), but you can just focus on the main function, where next(series) gives you the next term of the summation.
This gives me the output:
math.e: 2.71828182845904509080.
Stepwise: 2.71828182845904553488.
Tolerated: 2.71828182844675936281.
The most effective way is to use the properties of the exponential function.
exp(x)=(exp(x/N))^N
Thus you compute x=exp(2^(-n)) with 2n bits more precision than required in the final result, and compute e by squaring the result n times.
For small numbers x, the error of truncating the series for exp(x) at the term with power m-1 is smaller than two times the next term with power m.
To summarize, to compute e with a precision/accuracy of d bit, you select some medium large n and select m such that
2^(1-mn)/m! is smaller than 2^(-d-2n)
This determination of m can also be done dynamically (using Decimal as in the answer of user22698)
from decimal import Decimal, getcontext
def eulernumber(d):
dd=d
n=4
while dd > 1:
dd /= 8
n += 1
getcontext().prec = d+n
x = Decimal(1)/Decimal(1 << n)
eps = Decimal(1)/Decimal(1 << (1 + (10*d)/3 ))
term = x
expsum = Decimal(1) + x
m = 2
while term > eps:
term *= x / Decimal(m)
m += 1
expsum += term
for k in range(n):
expsum *= expsum
getcontext().prec = d
expsum += Decimal(0)
return expsum
if __name__ == "__main__":
for k in range(1,6):
print(k,eulernumber(4*k))
for k in range(10,13):
print(k,eulernumber(4*k))
with output
( 1, Decimal('2.718'))
( 2, Decimal('2.7182818'))
( 3, Decimal('2.71828182846'))
( 4, Decimal('2.718281828459045'))
( 5, Decimal('2.7182818284590452354'))
(10, Decimal('2.718281828459045235360287471352662497757'))
(11, Decimal('2.7182818284590452353602874713526624977572471'))
(12, Decimal('2.71828182845904523536028747135266249775724709370'))
See the (unix/posix) bc math library for a more professional implementation of this idea, also for the logarithm and trig functions. The code of the exponential function is even given as example in the man page.

Solving a difficult (polynomial?) equation in Python

I am new to programming (Python is my first language) but I love to design algorithms. I am currently working on a system of equations (integers) and I cannot find any references to solving my particular problem.
Let me explain.
I have an equation (a test, if you will):
raw_input == [(90*x + a) * y] + z
where a is some constant.
My problem is, the variable z counts in a manner very similar to a Fibonacci sequence, and the variable x is the step of z. So what I mean by this (for a Fibonacci sequence) is that at the first term of the z sequence, x = 0, and at the second term of the z sequence, x = 1. I need to solve for y.
The exact process for determining z is as follows
where c and d are constants:
#at x = 0
temp = (c+(90*x)) * (d+(90*x))
temp/90 = z(0)
#at x = 1
new_temp = (c+(90*x)) * (d + (90*x))
new_temp/90 = z(1)
#for all the rest of the values of z (and x), use:
j = z(# x=1) - z(# x=0)
k = j + 180
l = z(# x=1) + k
print "z(# x=1) - z(# x=0) = j"
print "j + 180 = k"
print "k + z(1) = l"
repeat until z > raw_input
this creates the spread of z values by the relation:
j = z(# x=n) - z(# x=n-1)
k = j + 180
l = k + z(# x = n)
I need to scan through (skip) the values of z < x to test for the condition of a whole-number solution for y.
Does this seem possible?
It seems your best approach would be to recast the given equation as a recurrence relation and then either define a recursive function to determine the values you desire to compute or find the closed form solution to the relation. For more information on recurrence relations see:
Any decent book on Combinatorics
Wikipedia: Recurrence relation. Particularly, the sections:
2.1: Linear homogeneous recurrence relations with constant coefficients
2.2: Rational generating function
3.1: Solving recurrence relations, General Methods
Though the general methods for solving recurrence relations are reasonably able, the most powerful technique is the z-transform: 3.3: Solving with z-transforms
3.5: Solving non-homogeneous recurrence relations. The techniques and discussion in the rest of the article are mostly suited for pure applications, but may occasionally find practical uses as well.
WolframMathWorld: Recurrence equation
Finally, in my experience, such problems are best tackled with mathematical numerical analysis software such as MatLab, Octave,or Mathematica. At the very least, with these you have a platform which enables rapid deployment and testing.
All I've done is translate your psuedo-code into Python. Maybe it can be of some help. Perhaps you should have a look at the Python tutorial if you haven't already.
# python 2.7
# raw_input returns string - convert to int
upper_bound = int(raw_input('Upper bound: '))
def z(x):
'A function to calculate z from x.'
# c and d are constants
c = 5
d = 2
# integer division here
return (c + 90*x)*(d + 90*x)/90
# the value of z_0
z0 = z_x = z(0)
# a list to hold the z values z_0, z_1, ...
# the list includes z_0 (when x = 0)
zs = [z0]
x = 1
while z_x < upper_bound:
z_x = z(x)
zs.append(z_x)
j = zs[x] - zs[x - 1]
k = j + 180
l = zs[x] + k
print j, k, l
x += 1

Categories

Resources