package fixed import ( "math" "math/rand" "testing" ) func near(t *testing.T, name string, got F, want, tol float64) { t.Helper() if d := math.Abs(got.Float64() - want); d > tol { t.Errorf("%s: got %v want %v (diff %g)", name, got.Float64(), want, d) } } func TestMulDiv(t *testing.T) { near(t, "mul", FromInt(3).Mul(FromRatio(1, 2)), 1.5, 1e-9) near(t, "mulneg", FromInt(-3).Mul(FromRatio(1, 2)), -1.5, 1e-9) near(t, "div", FromInt(1).Div(FromInt(3)), 1.0/3, 1e-9) near(t, "divneg", FromInt(-7).Div(FromInt(2)), -3.5, 1e-9) if FromInt(1<<30).Mul(FromInt(1<<30)) != Max { t.Error("expected saturation") } if FromInt(1).Div(0) != Max || FromInt(-1).Div(0) != Min { t.Error("div by zero should saturate") } } func TestSqrt(t *testing.T) { for _, v := range []float64{0.25, 1, 2, 3, 100, 1e6, 2e9} { f := F(v * float64(One)) near(t, "sqrt", f.Sqrt(), math.Sqrt(v), 1e-8) } if IntSqrt(1<<62) != 1<<31 || IntSqrt(99) != 9 { t.Error("IntSqrt") } } func TestSinCos(t *testing.T) { r := rand.New(rand.NewSource(1)) for i := 0; i < 2000; i++ { a := (r.Float64() - 0.5) * 40 f := F(a * float64(One)) s, c := SinCos(f) af := f.Float64() near(t, "sin", s, math.Sin(af), 2e-9) near(t, "cos", c, math.Cos(af), 2e-9) } } func TestAtan2(t *testing.T) { r := rand.New(rand.NewSource(2)) for i := 0; i < 2000; i++ { x, y := (r.Float64()-0.5)*1e6, (r.Float64()-0.5)*1e6 got := Atan2(F(y*float64(One)), F(x*float64(One))) near(t, "atan2", got, math.Atan2(y, x), 2e-8) } near(t, "atan2(0,-1)", Atan2(0, -One), math.Pi, 1e-8) } func TestVec3(t *testing.T) { // A 3-4-12 vector has length 13; scale it far past what a naive x*x would allow. v := Vec{FromInt(3_000_000), FromInt(4_000_000), FromInt(12_000_000)} near(t, "norm3", v.Len(), 13_000_000, 1e-6) u := Vec{FromInt(3), FromInt(4), FromInt(12)}.Unit() near(t, "unit", u.Len(), 1, 1e-8) d := FromSpherical(FromRatio(1, 2), FromRatio(3, 10)) near(t, "spherical len", d.Len(), 1, 1e-8) near(t, "spherical z", d.Z, math.Sin(0.3), 1e-8) near(t, "spherical x", d.X, math.Cos(0.3)*math.Cos(0.5), 1e-8) r := Vec{One, 0, 0}.RotateX(HalfPi).RotateZ(HalfPi) near(t, "rot x", r.X, 0, 1e-8) near(t, "rot y", r.Y, 1, 1e-8) r = Vec{0, One, 0}.RotateX(HalfPi) near(t, "rotx z", r.Z, 1, 1e-8) }