Browse Source

Improve core:math procedures and add loads of unit tests

gingerBill 2 years ago
parent
commit
0c25f7cdc5
3 changed files with 884 additions and 14 deletions
  1. 42 12
      core/math/math.odin
  2. 7 1
      tests/common/common.odin
  3. 835 1
      tests/core/math/test_core_math.odin

+ 42 - 12
core/math/math.odin

@@ -1088,7 +1088,7 @@ is_nan       :: proc{
 // If sign < 0, is_inf reports whether f is negative infinity.
 // If sign == 0, is_inf reports whether f is either infinity.
 is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
-	class := classify(abs(x))
+	class := classify(x)
 	switch {
 	case sign > 0:
 		return class == .Inf
@@ -1105,7 +1105,7 @@ is_inf_f16be :: proc "contextless" (x: f16be, sign: int = 0) -> bool {
 }
 
 is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
-	class := classify(abs(x))
+	class := classify(x)
 	switch {
 	case sign > 0:
 		return class == .Inf
@@ -1122,7 +1122,7 @@ is_inf_f32be :: proc "contextless" (x: f32be, sign: int = 0) -> bool {
 }
 
 is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
-	class := classify(abs(x))
+	class := classify(x)
 	switch {
 	case sign > 0:
 		return class == .Inf
@@ -1344,20 +1344,20 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
 		}
 		return copy_sign(PI, y)
 	case x == 0:
-		return copy_sign(PI*0.5, y)
+		return copy_sign(PI/2, y)
 	case is_inf(x, 0):
 		if is_inf(x, 1) {
 			if is_inf(y, 0) {
-				return copy_sign(PI*0.25, y)
+				return copy_sign(PI/4, y)
 			}
 			return copy_sign(0, y)
 		}
 		if is_inf(y, 0) {
-			return copy_sign(PI*0.75, y)
+			return copy_sign(3*PI/4, y)
 		}
 		return copy_sign(PI, y)
 	case is_inf(y, 0):
-		return copy_sign(PI*0.5, y)
+		return copy_sign(PI/2, y)
 	}
 
 	q := atan(y / x)
@@ -1599,16 +1599,46 @@ acos :: proc{
 }
 
 sinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	return (exp(x) - exp(-x))*0.5
+	return copy_sign(((exp(x) - exp(-x))*0.5), x)
 }
 
 cosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	return (exp(x) + exp(-x))*0.5
+	return ((exp(x) + exp(-x))*0.5)
 }
 
-tanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	t := exp(2*x)
-	return (t - 1) / (t + 1)
+tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
+	P0 :: -9.64399179425052238628e-1
+	P1 :: -9.92877231001918586564e1
+	P2 :: -1.61468768441708447952e3
+	Q0 :: +1.12811678491632931402e2
+	Q1 :: +2.23548839060100448583e3
+	Q2 :: +4.84406305325125486048e3
+
+	MAXLOG :: 8.8029691931113054295988e+01 // log(2**127)
+
+
+	x := f64(y)
+	z := abs(x)
+	switch {
+	case z > 0.5*MAXLOG:
+		if x < 0 {
+			return -1
+		}
+		return 1
+	case z >= 0.625:
+		s := exp(2 * z)
+		z = 1 - 2/(s+1)
+		if x < 0 {
+			z = -z
+		}
+	case:
+		if x == 0 {
+			return T(x)
+		}
+		s := x * x
+		z = x + x*s*((P0*s+P1)*s+P2)/(((s+Q0)*s+Q1)*s+Q2)
+	}
+	return T(z)
 }
 
 asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {

+ 7 - 1
tests/common/common.odin

@@ -12,15 +12,21 @@ TEST_fail  := 0
 when ODIN_TEST {
 	expect  :: testing.expect
 	log     :: testing.log
+	errorf  :: testing.errorf
 } else {
 	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
 		TEST_count += 1
 		if !condition {
 			TEST_fail += 1
-			fmt.printf("[%v] FAIL %v\n", loc, message)
+			fmt.printf("[%v:%s] FAIL %v\n", loc, loc.procedure, message)
 			return
 		}
 	}
+	errorf  :: proc(t: ^testing.T, message: string, args: ..any, loc := #caller_location) {
+		TEST_fail += 1
+		fmt.printf("[%v:%s] Error %v\n", loc, loc.procedure, fmt.tprintf(message, ..args))
+		return
+	}
 	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
 		fmt.printf("[%v] ", loc)
 		fmt.printf("log: %v\n", v)

+ 835 - 1
tests/core/math/test_core_math.odin

@@ -9,7 +9,7 @@ import "core:testing"
 import tc "tests:common"
 
 main :: proc() {
-    t := testing.T{}
+	t := testing.T{}
 
 	test_classify_f16(&t)
 	test_classify_f32(&t)
@@ -19,6 +19,25 @@ main :: proc() {
 	test_trunc_f32(&t)
 	test_trunc_f64(&t)
 
+	test_nan(&t)
+	test_acos(&t)
+	test_acosh(&t)
+	test_asin(&t)
+	test_asinh(&t)
+	test_atan(&t)
+	test_atanh(&t)
+	test_atan2(&t)
+	test_cos(&t)
+	test_cosh(&t)
+	test_sin(&t)
+	test_sinh(&t)
+	test_sqrt(&t)
+	test_tan(&t)
+	test_tanh(&t)
+	test_large_cos(&t)
+	test_large_sin(&t)
+	test_large_tan(&t)
+
 	tc.report(&t)
 }
 
@@ -308,3 +327,818 @@ test_trunc_f64 :: proc(t: ^testing.T) {
 	r = trunc_f64(v)
 	tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
 }
+
+
+vf := []f64{
+	4.9790119248836735e+00,
+	7.7388724745781045e+00,
+	-2.7688005719200159e-01,
+	-5.0106036182710749e+00,
+	9.6362937071984173e+00,
+	2.9263772392439646e+00,
+	5.2290834314593066e+00,
+	2.7279399104360102e+00,
+	1.8253080916808550e+00,
+	-8.6859247685756013e+00,
+}
+
+// The expected results below were computed by the high precision calculators at https://keisan.casio.com/.
+acos := []f64{
+	1.0496193546107222142571536e+00,
+	6.8584012813664425171660692e-01,
+	1.5984878714577160325521819e+00,
+	2.0956199361475859327461799e+00,
+	2.7053008467824138592616927e-01,
+	1.2738121680361776018155625e+00,
+	1.0205369421140629186287407e+00,
+	1.2945003481781246062157835e+00,
+	1.3872364345374451433846657e+00,
+	2.6231510803970463967294145e+00,
+}
+acosh := []f64{
+	2.4743347004159012494457618e+00,
+	2.8576385344292769649802701e+00,
+	7.2796961502981066190593175e-01,
+	2.4796794418831451156471977e+00,
+	3.0552020742306061857212962e+00,
+	2.044238592688586588942468e+00,
+	2.5158701513104513595766636e+00,
+	1.99050839282411638174299e+00,
+	1.6988625798424034227205445e+00,
+	2.9611454842470387925531875e+00,
+}
+asin := []f64{
+	5.2117697218417440497416805e-01,
+	8.8495619865825236751471477e-01,
+	-02.769154466281941332086016e-02,
+	-5.2482360935268931351485822e-01,
+	1.3002662421166552333051524e+00,
+	2.9698415875871901741575922e-01,
+	5.5025938468083370060258102e-01,
+	2.7629597861677201301553823e-01,
+	1.83559892257451475846656e-01,
+	-1.0523547536021497774980928e+00,
+}
+asinh := []f64{
+	2.3083139124923523427628243e+00,
+	2.743551594301593620039021e+00,
+	-2.7345908534880091229413487e-01,
+	-2.3145157644718338650499085e+00,
+	2.9613652154015058521951083e+00,
+	1.7949041616585821933067568e+00,
+	2.3564032905983506405561554e+00,
+	1.7287118790768438878045346e+00,
+	1.3626658083714826013073193e+00,
+	-2.8581483626513914445234004e+00,
+}
+atan := []f64{
+	1.372590262129621651920085e+00,
+	1.442290609645298083020664e+00,
+	-2.7011324359471758245192595e-01,
+	-1.3738077684543379452781531e+00,
+	1.4673921193587666049154681e+00,
+	1.2415173565870168649117764e+00,
+	1.3818396865615168979966498e+00,
+	1.2194305844639670701091426e+00,
+	1.0696031952318783760193244e+00,
+	-1.4561721938838084990898679e+00,
+}
+atanh := []f64{
+	5.4651163712251938116878204e-01,
+	1.0299474112843111224914709e+00,
+	-2.7695084420740135145234906e-02,
+	-5.5072096119207195480202529e-01,
+	1.9943940993171843235906642e+00,
+	3.01448604578089708203017e-01,
+	5.8033427206942188834370595e-01,
+	2.7987997499441511013958297e-01,
+	1.8459947964298794318714228e-01,
+	-1.3273186910532645867272502e+00,
+}
+atan2 := []f64{
+	1.1088291730037004444527075e+00,
+	9.1218183188715804018797795e-01,
+	1.5984772603216203736068915e+00,
+	2.0352918654092086637227327e+00,
+	8.0391819139044720267356014e-01,
+	1.2861075249894661588866752e+00,
+	1.0889904479131695712182587e+00,
+	1.3044821793397925293797357e+00,
+	1.3902530903455392306872261e+00,
+	2.2859857424479142655411058e+00,
+}
+cos := []f64{
+	2.634752140995199110787593e-01,
+	1.148551260848219865642039e-01,
+	9.6191297325640768154550453e-01,
+	2.938141150061714816890637e-01,
+	-9.777138189897924126294461e-01,
+	-9.7693041344303219127199518e-01,
+	4.940088096948647263961162e-01,
+	-9.1565869021018925545016502e-01,
+	-2.517729313893103197176091e-01,
+	-7.39241351595676573201918e-01,
+}
+
+// Results for 1e5 * Pi + vf[i]
+cosLarge := []f64{
+	2.634752141185559426744e-01,
+	1.14855126055543100712e-01,
+	9.61912973266488928113e-01,
+	2.9381411499556122552e-01,
+	-9.777138189880161924641e-01,
+	-9.76930413445147608049e-01,
+	4.940088097314976789841e-01,
+	-9.15658690217517835002e-01,
+	-2.51772931436786954751e-01,
+	-7.3924135157173099849e-01,
+}
+
+cosh := []f64{
+	7.2668796942212842775517446e+01,
+	1.1479413465659254502011135e+03,
+	1.0385767908766418550935495e+00,
+	7.5000957789658051428857788e+01,
+	7.655246669605357888468613e+03,
+	9.3567491758321272072888257e+00,
+	9.331351599270605471131735e+01,
+	7.6833430994624643209296404e+00,
+	3.1829371625150718153881164e+00,
+	2.9595059261916188501640911e+03,
+}
+
+sin := []f64{
+	-9.6466616586009283766724726e-01,
+	9.9338225271646545763467022e-01,
+	-2.7335587039794393342449301e-01,
+	9.5586257685042792878173752e-01,
+	-2.099421066779969164496634e-01,
+	2.135578780799860532750616e-01,
+	-8.694568971167362743327708e-01,
+	4.019566681155577786649878e-01,
+	9.6778633541687993721617774e-01,
+	-6.734405869050344734943028e-01,
+}
+
+// Results for 1e5 * Pi + vf[i]
+sinLarge := []f64{
+	-9.646661658548936063912e-01,
+	9.933822527198506903752e-01,
+	-2.7335587036246899796e-01,
+	9.55862576853689321268e-01,
+	-2.099421066862688873691e-01,
+	2.13557878070308981163e-01,
+	-8.694568970959221300497e-01,
+	4.01956668098863248917e-01,
+	9.67786335404528727927e-01,
+	-6.7344058693131973066e-01,
+}
+sinh := []f64{
+	7.2661916084208532301448439e+01,
+	1.1479409110035194500526446e+03,
+	-2.8043136512812518927312641e-01,
+	-7.499429091181587232835164e+01,
+	7.6552466042906758523925934e+03,
+	9.3031583421672014313789064e+00,
+	9.330815755828109072810322e+01,
+	7.6179893137269146407361477e+00,
+	3.021769180549615819524392e+00,
+	-2.95950575724449499189888e+03,
+}
+sqrt := []f64{
+	2.2313699659365484748756904e+00,
+	2.7818829009464263511285458e+00,
+	5.2619393496314796848143251e-01,
+	2.2384377628763938724244104e+00,
+	3.1042380236055381099288487e+00,
+	1.7106657298385224403917771e+00,
+	2.286718922705479046148059e+00,
+	1.6516476350711159636222979e+00,
+	1.3510396336454586262419247e+00,
+	2.9471892997524949215723329e+00,
+}
+tan := []f64{
+	-3.661316565040227801781974e+00,
+	8.64900232648597589369854e+00,
+	-2.8417941955033612725238097e-01,
+	3.253290185974728640827156e+00,
+	2.147275640380293804770778e-01,
+	-2.18600910711067004921551e-01,
+	-1.760002817872367935518928e+00,
+	-4.389808914752818126249079e-01,
+	-3.843885560201130679995041e+00,
+	9.10988793377685105753416e-01,
+}
+
+// Results for 1e5 * Pi + vf[i]
+tanLarge := []f64{
+	-3.66131656475596512705e+00,
+	8.6490023287202547927e+00,
+	-2.841794195104782406e-01,
+	3.2532901861033120983e+00,
+	2.14727564046880001365e-01,
+	-2.18600910700688062874e-01,
+	-1.760002817699722747043e+00,
+	-4.38980891453536115952e-01,
+	-3.84388555942723509071e+00,
+	9.1098879344275101051e-01,
+}
+tanh := []f64{
+	9.9990531206936338549262119e-01,
+	9.9999962057085294197613294e-01,
+	-2.7001505097318677233756845e-01,
+	-9.9991110943061718603541401e-01,
+	9.9999999146798465745022007e-01,
+	9.9427249436125236705001048e-01,
+	9.9994257600983138572705076e-01,
+	9.9149409509772875982054701e-01,
+	9.4936501296239685514466577e-01,
+	-9.9999994291374030946055701e-01,
+}
+
+NaN :: 0h7fff_ffff_ffff_ffff
+Pi  :: 0h4009_21fb_5444_2d18
+
+// arguments and expected results for special cases
+vfacos_sc := []f64{
+	-Pi,
+	1,
+	Pi,
+	NaN,
+}
+acos_sc := []f64{
+	NaN,
+	0,
+	NaN,
+	NaN,
+}
+
+vfacosh_sc := []f64{
+	math.inf_f64(-1),
+	0.5,
+	1,
+	math.inf_f64(1),
+	NaN,
+}
+acosh_sc := []f64{
+	NaN,
+	NaN,
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+
+vfasin_sc := []f64{
+	-Pi,
+	math.copy_sign_f64(0, -1),
+	0,
+	Pi,
+	NaN,
+}
+asin_sc := []f64{
+	NaN,
+	math.copy_sign_f64(0, -1),
+	0,
+	NaN,
+	NaN,
+}
+
+vfasinh_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+asinh_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+
+vfatan_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+atan_sc := []f64{
+	-Pi / 2,
+	math.copy_sign_f64(0, -1),
+	0,
+	Pi / 2,
+	NaN,
+}
+
+vfatanh_sc := []f64{
+	math.inf_f64(-1),
+	-Pi,
+	-1,
+	math.copy_sign_f64(0, -1),
+	0,
+	1,
+	Pi,
+	math.inf_f64(1),
+	NaN,
+}
+atanh_sc := []f64{
+	NaN,
+	NaN,
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+	NaN,
+	NaN,
+}
+vfatan2_sc := [][2]f64{
+	{math.inf_f64(-1), math.inf_f64(-1)},
+	{math.inf_f64(-1), -Pi},
+	{math.inf_f64(-1), 0},
+	{math.inf_f64(-1), +Pi},
+	{math.inf_f64(-1), math.inf_f64(1)},
+	{math.inf_f64(-1), NaN},
+	{-Pi, math.inf_f64(-1)},
+	{-Pi, 0},
+	{-Pi, math.inf_f64(1)},
+	{-Pi, NaN},
+	{math.copy_sign_f64(0, -1), math.inf_f64(-1)},
+	{math.copy_sign_f64(0, -1), -Pi},
+	{math.copy_sign_f64(0, -1), math.copy_sign_f64(0, -1)},
+	{math.copy_sign_f64(0, -1), 0},
+	{math.copy_sign_f64(0, -1), +Pi},
+	{math.copy_sign_f64(0, -1), math.inf_f64(1)},
+	{math.copy_sign_f64(0, -1), NaN},
+	{0, math.inf_f64(-1)},
+	{0, -Pi},
+	{0, math.copy_sign_f64(0, -1)},
+	{0, 0},
+	{0, +Pi},
+	{0, math.inf_f64(1)},
+	{0, NaN},
+	{+Pi, math.inf_f64(-1)},
+	{+Pi, 0},
+	{+Pi, math.inf_f64(1)},
+	{1.0, math.inf_f64(1)},
+	{-1.0, math.inf_f64(1)},
+	{+Pi, NaN},
+	{math.inf_f64(1), math.inf_f64(-1)},
+	{math.inf_f64(1), -Pi},
+	{math.inf_f64(1), 0},
+	{math.inf_f64(1), +Pi},
+	{math.inf_f64(1), math.inf_f64(1)},
+	{math.inf_f64(1), NaN},
+	{NaN, NaN},
+}
+atan2_sc := []f64{
+	-3 * Pi / 4,     // atan2(-Inf, -Inf)
+	-Pi / 2,         // atan2(-Inf, -Pi)
+	-Pi / 2,         // atan2(-Inf, +0)
+	-Pi / 2,         // atan2(-Inf, +Pi)
+	-Pi / 4,         // atan2(-Inf, +Inf)
+	NaN,           // atan2(-Inf, NaN)
+	-Pi,             // atan2(-Pi, -Inf)
+	-Pi / 2,         // atan2(-Pi, +0)
+	math.copy_sign_f64(0, -1), // atan2(-Pi, Inf)
+	NaN,           // atan2(-Pi, NaN)
+	-Pi,             // atan2(-0, -Inf)
+	-Pi,             // atan2(-0, -Pi)
+	-Pi,             // atan2(-0, -0)
+	math.copy_sign_f64(0, -1), // atan2(-0, +0)
+	math.copy_sign_f64(0, -1), // atan2(-0, +Pi)
+	math.copy_sign_f64(0, -1), // atan2(-0, +Inf)
+	NaN,           // atan2(-0, NaN)
+	Pi,              // atan2(+0, -Inf)
+	Pi,              // atan2(+0, -Pi)
+	Pi,              // atan2(+0, -0)
+	0,               // atan2(+0, +0)
+	0,               // atan2(+0, +Pi)
+	0,               // atan2(+0, +Inf)
+	NaN,           // atan2(+0, NaN)
+	Pi,              // atan2(+Pi, -Inf)
+	Pi / 2,          // atan2(+Pi, +0)
+	0,               // atan2(+Pi, +Inf)
+	0,               // atan2(+1, +Inf)
+	math.copy_sign_f64(0, -1), // atan2(-1, +Inf)
+	NaN,           // atan2(+Pi, NaN)
+	3 * Pi / 4,      // atan2(+Inf, -Inf)
+	Pi / 2,          // atan2(+Inf, -Pi)
+	Pi / 2,          // atan2(+Inf, +0)
+	Pi / 2,          // atan2(+Inf, +Pi)
+	Pi / 4,          // atan2(+Inf, +Inf)
+	NaN,           // atan2(+Inf, NaN)
+	NaN,           // atan2(NaN, NaN)
+}
+
+vfcbrt_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+cbrt_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+
+vfceil_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+ceil_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+
+vfcopysign_sc := []f64{
+	math.inf_f64(-1),
+	math.inf_f64(1),
+	NaN,
+}
+copysign_sc := []f64{
+	math.inf_f64(-1),
+	math.inf_f64(-1),
+	NaN,
+}
+
+vfcos_sc := []f64{
+	math.inf_f64(-1),
+	math.inf_f64(1),
+	NaN,
+}
+cos_sc := []f64{
+	NaN,
+	NaN,
+	NaN,
+}
+
+vfcosh_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+cosh_sc := []f64{
+	math.inf_f64(1),
+	1,
+	1,
+	math.inf_f64(1),
+	NaN,
+}
+
+
+vfsin_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+sin_sc := []f64{
+	NaN,
+	math.copy_sign_f64(0, -1),
+	0,
+	NaN,
+	NaN,
+}
+
+vfsinh_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+sinh_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+
+vftanh_sc := []f64{
+	math.inf_f64(-1),
+	math.copy_sign_f64(0, -1),
+	0,
+	math.inf_f64(1),
+	NaN,
+}
+tanh_sc := []f64{
+	-1,
+	math.copy_sign_f64(0, -1),
+	0,
+	1,
+	NaN,
+}
+
+
+tolerance :: proc(a, b, e: f64) -> bool {
+	// Multiplying by e here can underflow denormal values to zero.
+	// Check a==b so that at least if a and b are small and identical
+	// we say they match.
+	if a == b {
+		return true
+	}
+	e := e
+	d := a - b
+	if d < 0 {
+		d = -d
+	}
+
+	// note: b is correct (expected) value, a is actual value.
+	// make error tolerance a fraction of b, not a.
+	if b != 0 {
+		e = e * b
+		if e < 0 {
+			e = -e
+		}
+	}
+	return d < e
+}
+close :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool {
+	ok := tolerance(a, b, 1e-9)
+	// tc.expect(t, ok, fmt.tprintf("%.15g is not close to %.15g", a, b), loc)
+	return ok
+}
+veryclose :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool {
+	ok := tolerance(a, b, 4e-14)
+	// tc.expect(t, ok, fmt.tprintf("%.15g is not veryclose to %.15g", a, b), loc)
+	return ok
+}
+soclose :: proc(t: ^testing.T, a, b, e: f64, loc := #caller_location) -> bool {
+	ok := tolerance(a, b, e)
+	// tc.expect(t, ok, fmt.tprintf("%.15g is not soclose to %.15g", a, b), loc)
+	return ok
+}
+alike :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool {
+	ok := false
+	switch {
+	case math.is_nan(a) && math.is_nan(b):
+		ok = true
+	case a == b:
+		ok = math.signbit(a) == math.signbit(b)
+	}
+	// tc.expect(t, ok, fmt.tprintf("%.15g is not alike to %.15g", a, b), loc)
+	return ok
+}
+
+@test
+test_nan :: proc(t: ^testing.T) {
+	float64 := NaN
+	if float64 == float64 {
+		tc.errorf(t, "NaN returns %.15g, expected NaN", float64)
+	}
+	float32 := f32(float64)
+	if float32 == float32 {
+		tc.errorf(t, "float32(NaN) is %.15g, expected NaN", float32)
+	}
+}
+
+@test
+test_acos :: proc(t: ^testing.T) {
+	for _, i in vf {
+		a := vf[i] / 10
+		if f := math.acos(a); !close(t, acos[i], f) {
+			tc.errorf(t, "math.acos(%.15g) = %.15g, want %.15g", a, f, acos[i])
+		}
+	}
+	for _, i in vfacos_sc {
+		if f := math.acos(vfacos_sc[i]); !alike(t, acos_sc[i], f) {
+			tc.errorf(t, "math.acos(%.15g) = %.15g, want %.15g", vfacos_sc[i], f, acos_sc[i])
+		}
+	}
+}
+
+@test
+test_acosh :: proc(t: ^testing.T) {
+	for _, i in vf {
+		a := 1 + abs(vf[i])
+		if f := math.acosh(a); !veryclose(t, acosh[i], f) {
+			tc.errorf(t, "math.acosh(%.15g) = %.15g, want %.15g", a, f, acosh[i])
+		}
+	}
+	for _, i in vfacosh_sc {
+		if f := math.acosh(vfacosh_sc[i]); !alike(t, acosh_sc[i], f) {
+			tc.errorf(t, "math.acosh(%.15g) = %.15g, want %.15g", vfacosh_sc[i], f, acosh_sc[i])
+		}
+	}
+}
+
+@test
+test_asin :: proc(t: ^testing.T) {
+	for _, i in vf {
+		a := vf[i] / 10
+		if f := math.asin(a); !veryclose(t, asin[i], f) {
+			tc.errorf(t, "math.asin(%.15g) = %.15g, want %.15g", a, f, asin[i])
+		}
+	}
+	for _, i in vfasin_sc {
+		if f := math.asin(vfasin_sc[i]); !alike(t, asin_sc[i], f) {
+			tc.errorf(t, "math.asin(%.15g) = %.15g, want %.15g", vfasin_sc[i], f, asin_sc[i])
+		}
+	}
+}
+
+@test
+test_asinh :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.asinh(vf[i]); !veryclose(t, asinh[i], f) {
+			tc.errorf(t, "math.asinh(%.15g) = %.15g, want %.15g", vf[i], f, asinh[i])
+		}
+	}
+	for _, i in vfasinh_sc {
+		if f := math.asinh(vfasinh_sc[i]); !alike(t, asinh_sc[i], f) {
+			tc.errorf(t, "math.asinh(%.15g) = %.15g, want %.15g", vfasinh_sc[i], f, asinh_sc[i])
+		}
+	}
+}
+
+@test
+test_atan :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.atan(vf[i]); !veryclose(t, atan[i], f) {
+			tc.errorf(t, "math.atan(%.15g) = %.15g, want %.15g", vf[i], f, atan[i])
+		}
+	}
+	for _, i in vfatan_sc {
+		if f := math.atan(vfatan_sc[i]); !alike(t, atan_sc[i], f) {
+			tc.errorf(t, "math.atan(%.15g) = %.15g, want %.15g", vfatan_sc[i], f, atan_sc[i])
+		}
+	}
+}
+
+@test
+test_atanh :: proc(t: ^testing.T) {
+	for _, i in vf {
+		a := vf[i] / 10
+		if f := math.atanh(a); !veryclose(t, atanh[i], f) {
+			tc.errorf(t, "math.atanh(%.15g) = %.15g, want %.15g", a, f, atanh[i])
+		}
+	}
+	for _, i in vfatanh_sc {
+		if f := math.atanh(vfatanh_sc[i]); !alike(t, atanh_sc[i], f) {
+			tc.errorf(t, "math.atanh(%.15g) = %.15g, want %.15g", vfatanh_sc[i], f, atanh_sc[i])
+		}
+	}
+}
+
+@test
+test_atan2 :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.atan2(10, vf[i]); !veryclose(t, atan2[i], f) {
+			tc.errorf(t, "math.atan2(10, %.15g) = %.15g, want %.15g", vf[i], f, atan2[i])
+		}
+	}
+	for _, i in vfatan2_sc {
+		if f := math.atan2(vfatan2_sc[i][0], vfatan2_sc[i][1]); !alike(t, atan2_sc[i], f) {
+			tc.errorf(t, "math.atan2(%.15g, %.15g) = %.15g, want %.15g", vfatan2_sc[i][0], vfatan2_sc[i][1], f, atan2_sc[i])
+		}
+	}
+}
+
+@test
+test_cos :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.cos(vf[i]); !veryclose(t, cos[i], f) {
+			tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vf[i], f, cos[i])
+		}
+	}
+	for _, i in vfcos_sc {
+		if f := math.cos(vfcos_sc[i]); !alike(t, cos_sc[i], f) {
+			tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vfcos_sc[i], f, cos_sc[i])
+		}
+	}
+}
+
+@test
+test_cosh :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.cosh(vf[i]); !close(t, cosh[i], f) {
+			tc.errorf(t, "math.cosh(%.15g) = %.15g, want %.15g", vf[i], f, cosh[i])
+		}
+	}
+	for _, i in vfcosh_sc {
+		if f := math.cosh(vfcosh_sc[i]); !alike(t, cosh_sc[i], f) {
+			tc.errorf(t, "math.cosh(%.15g) = %.15g, want %.15g", vfcosh_sc[i], f, cosh_sc[i])
+		}
+	}
+}
+
+@test
+test_sin :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.sin(vf[i]); !veryclose(t, sin[i], f) {
+			tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vf[i], f, sin[i])
+		}
+	}
+	for _, i in vfsin_sc {
+		if f := math.sin(vfsin_sc[i]); !alike(t, sin_sc[i], f) {
+			tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vfsin_sc[i], f, sin_sc[i])
+		}
+	}
+}
+
+@test
+test_sinh :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.sinh(vf[i]); !close(t, sinh[i], f) {
+			tc.errorf(t, "math.sinh(%.15g) = %.15g, want %.15g", vf[i], f, sinh[i])
+		}
+	}
+	for _, i in vfsinh_sc {
+		if f := math.sinh(vfsinh_sc[i]); !alike(t, sinh_sc[i], f) {
+			tc.errorf(t, "math.sinh(%.15g) = %.15g, want %.15g", vfsinh_sc[i], f, sinh_sc[i])
+		}
+	}
+}
+
+@test
+test_sqrt :: proc(t: ^testing.T) {
+	for _, i in vf {
+		a := abs(vf[i])
+		if f := math.sqrt(a); !veryclose(t, sqrt[i], f) {
+			tc.errorf(t, "math.sqrt(%.15g) = %.15g, want %.15g", a, f, sqrt[i])
+		}
+	}
+}
+
+@test
+test_tan :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.tan(vf[i]); !veryclose(t, tan[i], f) {
+			tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vf[i], f, tan[i])
+		}
+	}
+	// same special cases as Sin
+	for _, i in vfsin_sc {
+		if f := math.tan(vfsin_sc[i]); !alike(t, sin_sc[i], f) {
+			tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vfsin_sc[i], f, sin_sc[i])
+		}
+	}
+}
+
+@test
+test_tanh :: proc(t: ^testing.T) {
+	for _, i in vf {
+		if f := math.tanh(vf[i]); !veryclose(t, tanh[i], f) {
+			tc.errorf(t, "math.tanh(%.15g) = %.15g, want %.15g", vf[i], f, tanh[i])
+		}
+	}
+	for _, i in vftanh_sc {
+		if f := math.tanh(vftanh_sc[i]); !alike(t, tanh_sc[i], f) {
+			tc.errorf(t, "math.tanh(%.15g) = %.15g, want %.15g", vftanh_sc[i], f, tanh_sc[i])
+		}
+	}
+}
+
+@test
+test_large_cos :: proc(t: ^testing.T) {
+	large := f64(1e5 * Pi)
+	for _, i in vf {
+		f1 := cosLarge[i]
+		f2 := math.cos(vf[i] + large)
+		if !close(t, f1, f2) {
+			tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
+		}
+	}
+}
+
+@test
+test_large_sin :: proc(t: ^testing.T) {
+	large := f64(1e5 * Pi)
+	for _, i in vf {
+		f1 := sinLarge[i]
+		f2 := math.sin(vf[i] + large)
+		if !close(t, f1, f2) {
+			tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
+		}
+	}
+}
+
+@test
+test_large_tan :: proc(t: ^testing.T) {
+	large := f64(1e5 * Pi)
+	for _, i in vf {
+		f1 := tanLarge[i]
+		f2 := math.tan(vf[i] + large)
+		if !close(t, f1, f2) {
+			tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
+		}
+	}
+}