// checkIsBestApprox32 checks that f is the best possible float32 // approximation of r. // Returns true on success. func checkIsBestApprox32(t *testing.T, f float32, r *Rat) bool { if math.Abs(float64(f)) >= math.MaxFloat32 { // Cannot check +Inf, -Inf, nor the float next to them (MaxFloat32). // But we have tests for these special cases. return true } // r must be strictly between f0 and f1, the floats bracketing f. f0 := math.Nextafter32(f, float32(math.Inf(-1))) f1 := math.Nextafter32(f, float32(math.Inf(+1))) // For f to be correct, r must be closer to f than to f0 or f1. df := delta(r, float64(f)) df0 := delta(r, float64(f0)) df1 := delta(r, float64(f1)) if df.Cmp(df0) > 0 { t.Errorf("Rat(%v).Float32() = %g (%b), but previous float32 %g (%b) is closer", r, f, f, f0, f0) return false } if df.Cmp(df1) > 0 { t.Errorf("Rat(%v).Float32() = %g (%b), but next float32 %g (%b) is closer", r, f, f, f1, f1) return false } if df.Cmp(df0) == 0 && !isEven32(f) { t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0) return false } if df.Cmp(df1) == 0 && !isEven32(f) { t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1) return false } return true }
func TestRound(t *testing.T) { ival := []float32{0} x := float32(1) for !math.IsInf(float64(x), 1) { ival = append(ival, x) x *= 2 } fval := []float32{} x = 0.5 for x != 0 { fval = append(fval, x, math.Nextafter32(x, 1), math.Nextafter32(x, -1)) x /= 2 } for _, i := range ival { for _, f := range fval { x := i + f y := Round(x) d := Abs(y - x) assert(d <= 0.5, "Round/Abs", t) assert(d < 0.5 || int(y)&1 == 0, "Round/Abs", t) assert(Round(-x) == -Round(x), "Round", t) assert(Signbit(Round(-x)) == !Signbit(y), "Signbit/Round", t) } } }
func TestRasterizeAlmostAxisAligned(t *testing.T) { z := NewRasterizer(8, 8) z.MoveTo(2, 2) z.LineTo(6, math.Nextafter32(2, 0)) z.LineTo(6, 6) z.LineTo(math.Nextafter32(2, 0), 6) z.ClosePath() dst := image.NewAlpha(z.Bounds()) z.Draw(dst, dst.Bounds(), image.Opaque, image.Point{}) if err := checkCornersCenter(dst); err != nil { t.Error(err) } }
func TestTrunc(t *testing.T) { ival := []float32{} x := float32(1) for !math.IsInf(float64(x), 1) { ival = append(ival, x) x *= 2 } fval := []float32{} x = math.Nextafter32(1, -1) for x != 0 { fval = append(fval, x) x = 1 - (1-x)*2 } for _, i := range ival { for _, f := range fval { x := i + f y := Trunc(x) assert(y == i || x-i == 1, "Trunc", t) z := Trunc(-x) assert(z == -y, "Trunc", t) assert(Signbit(Trunc(-x)) == !Signbit(y), "Signbit/Trunc", t) } } }
func init() { epsilon32 = math.Nextafter32(1, 2) - 1 }
func TestMarshalFloat(t *testing.T) { t.Parallel() nfail := 0 test := func(f float64, bits int) { vf := interface{}(f) if bits == 32 { f = float64(float32(f)) // round vf = float32(f) } bout, err := Marshal(vf) if err != nil { t.Errorf("Marshal(%T(%g)): %v", vf, vf, err) nfail++ return } out := string(bout) // result must convert back to the same float g, err := strconv.ParseFloat(out, bits) if err != nil { t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err) nfail++ return } if f != g || fmt.Sprint(f) != fmt.Sprint(g) { // fmt.Sprint handles ±0 t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf) nfail++ return } bad := badFloatREs if bits == 64 { bad = bad[:len(bad)-2] } for _, re := range bad { if re.MatchString(out) { t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re) nfail++ return } } } var ( bigger = math.Inf(+1) smaller = math.Inf(-1) ) var digits = "1.2345678901234567890123" for i := len(digits); i >= 2; i-- { for exp := -30; exp <= 30; exp++ { for _, sign := range "+-" { for bits := 32; bits <= 64; bits += 32 { s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp) f, err := strconv.ParseFloat(s, bits) if err != nil { log.Fatal(err) } next := math.Nextafter if bits == 32 { next = func(g, h float64) float64 { return float64(math.Nextafter32(float32(g), float32(h))) } } test(f, bits) test(next(f, bigger), bits) test(next(f, smaller), bits) if nfail > 50 { t.Fatalf("stopping test early") } } } } } test(0, 64) test(math.Copysign(0, -1), 64) test(0, 32) test(math.Copysign(0, -1), 32) }
func Nextafter32(x, y float32) float32 { return math.Nextafter32(x, y) }