Skip to content

Instantly share code, notes, and snippets.

@izabera
Last active January 19, 2025 12:36
Show Gist options
  • Save izabera/df0740b7f4544342c142100d90f96814 to your computer and use it in GitHub Desktop.
Save izabera/df0740b7f4544342c142100d90f96814 to your computer and use it in GitHub Desktop.
#!/bin/bash
# max multiple of pi so our maths always fits in a signed 64 bit int
pi=314159
scale=100000
# bhaskara's formula
# https://en.wikipedia.org/wiki/Bh%C4%81skara_I's_sine_approximation_formula
# avg err = 0.000836194
# max err = 0.00164
# wrongest = -1.37356
pi2=$((pi*pi))
cos () ((REPLY=((pi2-4*$1**2)*scale)/(pi2+$1**2)))
# equivalent to this
# cos () ((REPLY=((pisq-4*$1**2)*scale)/(pisq+$1**2)))
# sincos ()
# case $(($1/pi_2)) in
# 0) cos "$1" ; cos=$REPLY ; cos "$((pi_2-$1))" ; sin=$REPLY ;;
# 1) cos "$((pi-$1))" ; cos=$((-REPLY)); cos "$(($1-pi_2))" ; sin=$REPLY ;;
# 2) cos "$(($1-pi))" ; cos=$((-REPLY)); cos "$((pi_2*3-$1))"; sin=$((-REPLY)) ;;
# 3) cos "$(($1-pi2))"; cos=$REPLY ; cos "$((pi_2*3-$1))"; sin=$((-REPLY)) ;;
# esac
cosine='(pisq-4*y*y)*scale/(pisq+y*y)'
coscalc=(x pi-x x-pi x-pi2 )
sincalc=(pi_2-x x-pi_2 pi3_2-x pi3_2-x)
cosmult=(1 -1 -1 1)
sinmult=(1 1 -1 -1)
# sincos () ((q=$1/pi_2,x=$1,y=coscalc[q],cos=cosine*cosmult[q],y=sincalc[q],sin=cosine*sinmult[q]))
sincos=(
"y=${coscalc[0]},cos=${cosmult[0]%1}cosine,y=${sincalc[0]},sin=${sinmult[0]%1}cosine"
"y=${coscalc[1]},cos=${cosmult[1]%1}cosine,y=${sincalc[1]},sin=${sinmult[1]%1}cosine"
"y=${coscalc[2]},cos=${cosmult[2]%1}cosine,y=${sincalc[2]},sin=${sinmult[2]%1}cosine"
"y=${coscalc[3]},cos=${cosmult[3]%1}cosine,y=${sincalc[3]},sin=${sinmult[3]%1}cosine"
)
sincos () ((x=$1,sincos[$1/pi_2]))
# https://yal.cc/fast-atan2/
# arctan(x) ≈ π/4x − x(|x| − 1) × (0.2447 + 0.0663|x|), −1 ≤ x ≤ 1.
# avg err = 0.00097053
# max err = 0.00152
# wrongest = 0.47737
pi_4=78540
atan() ((x=$1,REPLY=(pi_4*x-(x*(x-scale)*(24470+6630*x/scale)/scale))/scale))
# test all the values in a range at this scaling factor
# and compare with an awk version that's as accurate as your libc
bash_loop() {
func=$1
for ((i=from;i<=to;i++)) do
"$func" "$i"
printf '%s\t%s\n' "$REPLY" "$i"
done
}
awk_loop () {
awk -v scale="$scale" -v from="$from" -v to="$to" "
BEGIN {
for (i = from; i <= to; i++)
print int($1*scale)
}"
}
compare () {
awk -v scale="$scale" -v display_all="$1" '
BEGIN {
OFS = "\t"
if (display_all)
print "error", "bash", "awk", "val"
}
{
awk = $1
bash = $2
val = $3
err = bash - awk
if (err < 0)
err = -err
total_err += err
if (err > max_err) {
max_err = err
wrongest_val = val
}
}
display_all { print err, bash, awk, $3 }
END {
print "avg err = " total_err / NR / scale
print "max err = " max_err / scale
print "wrongest = " wrongest_val / scale
}'
}
from=$((-pi/2)) to=$((pi/2))
echo cosine
paste <(awk_loop 'cos(i/scale)') <(bash_loop cos) | compare
from=0 to=$scale
echo arctan
paste <(awk_loop 'atan2(i,scale)') <(bash_loop atan) | compare
@izabera
Copy link
Author

izabera commented Dec 10, 2024

image
takes like 4.55µs per cosine on my machine
a bit less than that even, because of the overhead of the loop

@izabera
Copy link
Author

izabera commented Dec 10, 2024

image
atan in 5.8µs

@izabera
Copy link
Author

izabera commented Jan 19, 2025

image
sin+cos test, roughly 7µs for values between 0 and 2pi (https://gist.github.com/izabera/c6c51447e7b8f2460fbd0221624fa318)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment