If civilization collapses , then how do we calculate logarithm. I think that one can calculate logarithmn using only one knwn logarithm value log2(1.5).
I already know that log10(x) = ln(x)/ln(10)
So if we know log of any base, then we can calculate log10 of any number.
Therefore we should look for a base that is the easiest to calculate and the base is 2. I do wish to calculate the log of base 1 but I leave that puzzle to another day.
Mathematics will be rediscovered (or just not forgotten), and we can use the same approaches we used to, via iterative methods. See for example, this section of the Wikipedia page on logarithms.
(I’m not sure if this is intended as a joke, but if not, let me point out that the base-1 logarithm does not make sense. For e.g. \log_1(2), you’d need to find a real number x such that 1^x = 2, which obviously does not exist (mathematically speaking, f: \mathbb{R}^+ \to \mathbb{R}^+: t \mapsto 1^t (=1) is not surjective). Also, what would be \log_1(1)? There are many options (f is not injective either).)
So to calculate \log_2(1.75), given knowledge of \log_2(1.5), you only need to be able to calculate \log_2(7/3). But how did this simplify the problem? Also, what makes 1.5 special here? You could equally use any other positive number.
It’s possible that there’s some merit to your approach, if you can show that you are indeed simplifying the problem / obtaining a partial solution (e.g. digits in the binary or decimal expansion). If so, you’ll have (re)discovered an iterative method to calculate logarithms, like the ones linked to above.
maxiter = 64
function mylog2(level::Int64,x::Float64)
global maxiter
if level > maxiter
return 0.0
end
level += 1 # Increase the depth of function call
local adjustment::Int64=0
while x >= 3.0
adjustment += 1
x /= 2.0
end
while x < 1.5
adjustment -= 1
x *= 2.0
end
# at this point the value of x >= 1.5 and x < 3.0
if x == 1.5 # do the case when x is exactly 1.5
return Float64(adjustment) + log(2,1.5)
end
# at this point x > 1.5
# logB( x + y) = logB(x) + logB(1 + y/x)
# log2( 1.5 + d) = log2(1.5) + log2(1 + d/1.5)
# = log2(1.5) + log2(1) + log2(1 + (d/1.5)/1)
# = log2(1.5) + log2(1 + d/1.5)
#
# let z = 1 + d/1.5
# if z >= 1.5
# = log2(1.5) + mylog2(level,z)
# otherwise
# = log2(1.5) + 0.5 * mylog2(level,z^2)
#
local d,z
d = x - 1.5
z = 1 + d/1.5
if z >= 1.5
return Float64(adjustment) + log(2,1.5) + mylog2(level,z)
else
return Float64(adjustment) + log(2,1.5) + 0.5 * mylog2(level,z*z)
end
end
println("log2(4.5) = ",log(2,4.5) )
println("mylog2(0,4.5) = ",mylog2(0,4.5) )
# Sample output
#
# log2(4.5) = 2.1699250014423126
# mylog2(0,4.5) = 2.169925001442312
That indeed seems to work. Note that there is nothing special about 1.5: if you replace it with any other number x > 0 the approach is still valid. In particular, if you take x = 1, you don’t need to protect the value of \log_2(1.5) during the apocalypse, as you now simply have \log_2(x) = 0. This further makes the algorithm a bit easier, as z = x and you will always take the else branch.
In fact, with x = 1 you’ll have rediscovered the iterative approach described here.
Here is the version 2 of the source code. Yes, changing the konst from 1.5 to 1.4 works. It works as long as konst is greater than 1.0
it even works when konst is 2.0 which means that is nothing special about log2(1.5)
const maxiter = 64
const konst = 1.5
function mylog2(level::Int64,x::Float64)
global maxiter
global konst
if level > maxiter
return 0.0
end
level += 1 # Increase the depth of function call
local adjustment=0.0
while x >= 2*konst
adjustment += 1.0
x /= 2.0
end
while x < konst
adjustment -= 1.0
x *= 2.0
end
# at this point the value of x >= 1.5 and x < 3.0
if x == konst # do the case when x is exactly 1.5
return adjustment + log(2,konst)
end
# at this point x > 1.5
# logB( x + y) = logB(x) + logB(1 + y/x)
# log2( 1.5 + d) = log2(1.5) + log2(1 + d/1.5)
# = log2(1.5) + log2(1) + log2(1 + (d/1.5)/1)
# = log2(1.5) + log2(1 + d/1.5)
#
# let z = 1 + d/1.5
# if z >= 1.5
# = log2(1.5) + mylog2(level,z)
# otherwise
# = log2(1.5) + 0.5 * mylog2(level,z*z)
#
local d,z
d = x - konst
z = 1 + d/konst
if z >= konst
return adjustment + log(2,konst) + mylog2(level,z)
else
local numofsquaring = 0
local newz = z
while newz < konst
newz = newz*newz
numofsquaring += 1
end
return adjustment + log(2,konst) + 0.5^numofsquaring * mylog2(level,newz)
end
end
mylog2(x) = mylog2(0,x) # stub to have single arguement
println("log2(4.5) = ",log(2,4.5) )
println("mylog2(0,4.5) = ",mylog2(4.5) )
# Sample output
#
# log2(4.5) = 2.1699250014423126
# mylog2(0,4.5) = 2.169925001442312
I think you are abusing and overusing global and local, it’s not very Julia idiomatic style.
This results in the same:
function _mylog2(level::Integer, x; maxiter=64, konst=1.5)
if level > maxiter
return 0.0
end
level += 1 # Increase the depth of function call
adjustment=0.0
while x >= 2*konst
adjustment += 1.0
x /= 2.0
end
while x < konst
adjustment -= 1.0
x *= 2.0
end
if x == konst # do the case when x is exactly 1.5
return adjustment + log(2,konst)
end
d = x - konst
z = 1 + d / konst
if z >= konst
return adjustment + log(2,konst) + _mylog2(level,z)
else
numofsquaring = 0
newz = z
while newz < konst
newz = newz*newz
numofsquaring += 1
end
return adjustment + log(2,konst) + 0.5^numofsquaring * _mylog2(level,newz)
end
end
There is more ways to improve such as being generic to the input type T. and avoiding explicit values such as 2.0 which introduces conversions to Float64.
Here is version 3 of the source code which can handle different Floating Point Types.
using DoubleFloats
function mylog2(level::Integer,x;maxiter=64,konst=1.5)
local mytype = typeof(x)
local myone = one(mytype)
local mytwo = myone + myone
local mykonst = convert(mytype,konst)
if level > maxiter
return zero(mytype)
end
level += 1 # Increase the depth of function call
local adjustment = zero(mytype)
while x >= 2*mykonst
adjustment += myone
x /= mytwo
end
while x < mykonst
adjustment -= myone
x *= mytwo
end
# at this point the value of x >= 1.5 and x < 3.0
if x == mykonst # do the case when x is exactly 1.5
return adjustment + log(2,mykonst)
end
# at this point x > 1.5
local d,z
d = x - mykonst
z = myone + d/mykonst
if z >= convert(mytype,mykonst)
return adjustment + log(2,mykonst) + mylog2(level,z,maxiter=maxiter,konst=konst)
else
local numofsquaring = 0
local newz = z
while newz < mykonst
newz = newz*newz
numofsquaring += 1
end
return adjustment + log(2,mykonst) + (myone/mytwo)^numofsquaring * mylog2(level,newz,maxiter=maxiter,konst=konst)
end
end
mylog2(x) = mylog2(0,x) # stub to have single arguement
println("log2(4.5) = ",log(2,4.5) )
println("mylog2(0,4.5) = ",mylog2(4.5) )
println()
println("log2(4.5) = ",log(2,big(4.5)) )
println("mylog2(0,4.5) = ",mylog2(DoubleFloat(4.5)) )
println("mylog2(0,4.5) = ",mylog2(big(4.5)) )
# Sample output
#
# log2(4.5) = 2.1699250014423126
# mylog2(0,4.5) = 2.169925001442312
function mylog2(level::Integer, x::T;maxiter=64,konst=T(1.5)) where T
if level > maxiter
return zero(T)
end
level += 1 # Increase the depth of function call
adjustment = zero(T)
while x >= 2*konst
adjustment += T(1)
x /= T(2)
end
while x < konst
adjustment -= T(1)
x *= T(2)
end
# at this point the value of x >= 1.5 and x < 3.0
if x == konst # do the case when x is exactly 1.5
return adjustment + log(2,konst)
end
# at this point x > 1.5
d = x - konst
z = T(1) + d/konst
if z >= convert(T, konst)
return adjustment + log(2,konst) + mylog2(level,z,maxiter=maxiter,konst=konst)
else
numofsquaring = 0
newz = z
while newz < konst
newz = newz*newz
numofsquaring += 1
end
return adjustment + log(2,konst) + (T(1)/T(2))^numofsquaring * mylog2(level,newz,maxiter=maxiter,konst=konst)
end
end
Thanks for informing me that I can use T where T and I don’t need the local variables.
Here is version 4 of the source code
using DoubleFloats
function mylog2(level::Integer,x::T;maxiter=64,konst=T(1.5)) where T
# Sanity Checking x and konst must have the same type
if typeof(konst) != T
println("Failed Sanity Checking. x and konst must have the same type.")
return NaN
end
if level > maxiter
return zero(T)
end
level += 1 # Increase the depth of function call
adjustment = zero(T)
while x >= 2*konst
adjustment += one(T)
x /= T(2)
end
while x < konst
adjustment -= one(T)
x *= T(2)
end
# at this point the value of x >= 1.5 and x < 3.0
if x == konst # do the case when x is exactly 1.5
return adjustment + log(2,konst)
end
# at this point x > 1.5
local d,z
d = x - konst
z = one(T) + d/konst
if z >= konst
return adjustment + log(2,konst) + mylog2(level,z,maxiter=maxiter,konst=konst)
else
local numofsquaring = 0
local newz = z
while newz < konst
newz = newz*newz
numofsquaring += 1
end
return adjustment + log(2,konst) + T(0.5)^numofsquaring * mylog2(level,newz,maxiter=maxiter,konst=konst)
end
end
mylog2(x) = mylog2(0,x) # stub to have single arguement
println("log2(4.5) = ",log(2,4.5) )
println("mylog2(0,4.5) = ",mylog2(4.5) )
println()
println("log2(4.5) = ",log(2,big(4.5)) )
println("mylog2(0,4.5) = ",mylog2(DoubleFloat(4.5)) )
println("mylog2(0,4.5) = ",mylog2(big(4.5)) )
Example:
julia> mylog2(0,DoubleFloat(4.5),konst=1.2)
Failed Sanity Checking. x and konst must have the same type.
NaN