I implemented both circular and hyperbolic CORDIC algorithm in rotation mode:Z -> 0
In case of sin
and cos
which using circular implementation, the results are accurate. In case of sinh
and cosh
which is the hyperbolic algorithm, they are not.
The output of the code below (*_calc
is the CORDIC version, *_good
is the math.* version) is the following:
sin_good(20): 0.3420201433256687
sin_calc(20): 0.34202014332566866
sinh_good(20): 242582597.70489514
sinh_calc(20): 0.3555015499407712
cos_good(20): 0.9396926207859084
cos_calc(20): 0.9396926207859082
cosh_good(20): 242582597.70489514
cosh_calc(20): 1.0594692478629741
What am I doing wrong?
def lookup_circular(iteration):
return math.degrees(math.atan(2 ** -iteration))
def lookup_linear(iteration):
return 2 ** -iteration
def lookup_hyperbolic(iteration):
return math.degrees(math.atanh(2 ** -iteration))
def sin(angle):
x, y, z = cordic_circular_rotation_zto0(
x=1 / circular_scaling_factor(),
y=0,
z=float(angle)
)
return y
def cos(angle):
x, y, z = cordic_circular_rotation_zto0(
x=1 / circular_scaling_factor(),
y=0,
z=float(angle)
)
return x
def sinh(angle):
x, y, z = cordic_hyperbolic_rotation_zto0(
x=1 / hyperbolic_scaling_factor(),
y=0,
z=angle
)
return y
def cosh(angle):
x, y, z = cordic_hyperbolic_rotation_zto0(
x=1 / hyperbolic_scaling_factor(),
y=0,
z=angle
)
return x
def cordic_circular_rotation_zto0(x, y, z, n=64):
i = 0
while i <= n:
if z < 0:
newx = x + (y * 2.0 ** (-i))
newy = y - (x * 2.0 ** (-i))
z = z + lookup_circular(i)
else:
newx = x - (y * 2.0 ** (-i))
newy = y + (x * 2.0 ** (-i))
z = z - lookup_circular(i)
x = newx
y = newy
i += 1
return x, y, z
def cordic_hyperbolic_rotation_zto0(x, y, z, n=64):
i = 1
repeat = 4
while i <= n:
if z < 0:
newx = x - (y * 2.0 ** (-i))
newy = y - (x * 2.0 ** (-i))
z = z + lookup_hyperbolic(i)
else:
newx = x + (y * 2.0 ** (-i))
newy = y + (x * 2.0 ** (-i))
z = z - lookup_hyperbolic(i)
x = newx
y = newy
if i == repeat:
repeat = (i * 3) + 1
else:
i += 1
return x, y, z
def circular_scaling_factor(n=64):
e = 1
for i in range(0, n):
e = e * math.sqrt(1 + 2 ** (-2 * i))
return e
def hyperbolic_scaling_factor(n=64):
e = 1
for i in range(1, n):
e = e * math.sqrt(1 - 2 ** (-2 * i))
return e
if __name__ == '__main__':
angle = 20
sin_res = sin(angle)
print("sin_good({}): {}".format(angle, math.sin(math.radians(angle))))
print("sin_calc({}): {}".format(angle, sin_res))
print()
sinh_res = sinh(angle)
print("sinh_good({}): {}".format(angle, math.sinh(angle)))
print("sinh_calc({}): {}".format(angle, sinh_res))
print()
cos_res = cos(angle)
print("cos_good({}): {}".format(angle, math.cos(math.radians(angle))))
print("cos_calc({}): {}".format(angle, cos_res))
print()
cosh_res = cosh(angle)
print("cosh_good({}): {}".format(angle, math.cosh(angle)))
print("cosh_calc({}): {}".format(angle, cosh_res))
By removing the
math.degrees
of the inverse hyperbolic tan of lookup_hyperbolic, I find the following result for cosh:both functions match until x~1.1, and then the cordic function stays constant.
Which is what can be found in Digital Arithmetic - Ercegovac/Lang 2003 chapter 11
Same for sinh:
There is an extended cordic alogrithm that you could try to implement: