Last active
January 19, 2025 12:36
-
-
Save izabera/df0740b7f4544342c142100d90f96814 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
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
takes like 4.55µs per cosine on my machine
a bit less than that even, because of the overhead of the loop