mirror of https://github.com/liamg/aminal.git
309 lines
7.8 KiB
Go
309 lines
7.8 KiB
Go
// Copyright 2014 The go-gl Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package mgl32
|
|
|
|
import (
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestEqual(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var a float32 = 1.5
|
|
var b float32 = 1.0 + .5
|
|
|
|
if !FloatEqual(a, a) {
|
|
t.Errorf("Float Equal fails on comparing a number with itself")
|
|
}
|
|
|
|
if !FloatEqual(a, b) {
|
|
t.Errorf("Float Equal fails to compare two equivalent numbers with minimal drift")
|
|
} else if !FloatEqual(b, a) {
|
|
t.Errorf("Float Equal is not symmetric for some reason")
|
|
}
|
|
|
|
if !FloatEqual(0.0, 0.0) {
|
|
t.Errorf("Float Equal fails to compare zero values correctly")
|
|
}
|
|
|
|
if FloatEqual(1.5, 1.51) {
|
|
t.Errorf("Float Equal gives false positive on large difference")
|
|
}
|
|
|
|
if FloatEqual(1.5, 1.5000001) {
|
|
t.Errorf("Float Equal gives false positive on small difference")
|
|
}
|
|
|
|
if FloatEqual(1.5, 0.0) {
|
|
t.Errorf("Float Equal gives false positive comparing with zero")
|
|
}
|
|
}
|
|
|
|
func TestEqualThreshold(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// |1.0 - 1.01| < .1
|
|
if !FloatEqualThreshold(1.0, 1.01, 1e-1) {
|
|
t.Errorf("Thresholded equal returns negative on threshold")
|
|
}
|
|
|
|
// Comes out to |1.0 - 1.01| < .0001
|
|
if FloatEqualThreshold(1.0, 1.01, 1e-3) {
|
|
t.Errorf("Thresholded equal returns false positive on tolerant threshold")
|
|
}
|
|
}
|
|
|
|
func TestEqualThresholdTable(t *testing.T) {
|
|
// http://floating-point-gui.de/errors/NearlyEqualsTest.java
|
|
|
|
tests := []struct {
|
|
A, B, Ep float32
|
|
Expected bool
|
|
}{
|
|
{1.0, 1.01, 1e-1, true},
|
|
{1.0, 1.01, 1e-3, false},
|
|
|
|
// Regular large numbers
|
|
{1000000.0, 1000001.0, 0.00001, true},
|
|
{1000001.0, 1000000.0, 0.00001, true},
|
|
{10000.0, 10001.0, 0.00001, false},
|
|
{10001.0, 10000.0, 0.00001, false},
|
|
|
|
// Negative large numbers
|
|
{-1000000.0, -1000001.0, 0.00001, true},
|
|
{-1000001.0, -1000000.0, 0.00001, true},
|
|
{-10000.0, -10001.0, 0.00001, false},
|
|
{-10001.0, -10000.0, 0.00001, false},
|
|
|
|
// Numbers around 1
|
|
{1.0000001, 1.0000002, 0.00001, true},
|
|
{1.0000002, 1.0000001, 0.00001, true},
|
|
{1.0002, 1.0001, 0.00001, false},
|
|
{1.0001, 1.0002, 0.00001, false},
|
|
|
|
// Numbers around -1
|
|
{-1.000001, -1.000002, 0.00001, true},
|
|
{-1.000002, -1.000001, 0.00001, true},
|
|
{-1.0001, -1.0002, 0.00001, false},
|
|
{-1.0002, -1.0001, 0.00001, false},
|
|
|
|
// Numbers between 1 and 0
|
|
{0.000000001000001, 0.000000001000002, 0.00001, true},
|
|
{0.000000001000002, 0.000000001000001, 0.00001, true},
|
|
{0.000000000001002, 0.000000000001001, 0.00001, false},
|
|
{0.000000000001001, 0.000000000001002, 0.00001, false},
|
|
|
|
// Numbers between -1 and 0
|
|
{-0.000000001000001, -0.000000001000002, 0.00001, true},
|
|
{-0.000000001000002, -0.000000001000001, 0.00001, true},
|
|
{-0.000000000001002, -0.000000000001001, 0.00001, false},
|
|
{-0.000000000001001, -0.000000000001002, 0.00001, false},
|
|
|
|
// Comparisons involving zero
|
|
{0.0, 0.0, 0.00001, true},
|
|
{0.0, -0.0, 0.00001, true},
|
|
{-0.0, -0.0, 0.00001, true},
|
|
{0.00000001, 0.0, 0.00001, false},
|
|
{0.0, 0.00000001, 0.00001, false},
|
|
{-0.00000001, 0.0, 0.00001, false},
|
|
{0.0, -0.00000001, 0.00001, false},
|
|
|
|
// Comparisons involving infinities
|
|
{InfPos, InfPos, 0.00001, true},
|
|
{InfNeg, InfNeg, 0.00001, true},
|
|
{InfNeg, InfPos, 0.00001, false},
|
|
{InfPos, MaxValue, 0.00001, false},
|
|
{InfNeg, -MaxValue, 0.00001, false},
|
|
|
|
// Comparisons involving NaN values
|
|
{NaN, NaN, 0.00001, false},
|
|
{0.0, NaN, 0.00001, false},
|
|
{NaN, 0.0, 0.00001, false},
|
|
{-0.0, NaN, 0.00001, false},
|
|
{NaN, -0.0, 0.00001, false},
|
|
{NaN, InfPos, 0.00001, false},
|
|
{InfPos, NaN, 0.00001, false},
|
|
{NaN, InfNeg, 0.00001, false},
|
|
{InfNeg, NaN, 0.00001, false},
|
|
{NaN, MaxValue, 0.00001, false},
|
|
{MaxValue, NaN, 0.00001, false},
|
|
{NaN, -MaxValue, 0.00001, false},
|
|
{-MaxValue, NaN, 0.00001, false},
|
|
{NaN, MinValue, 0.00001, false},
|
|
{MinValue, NaN, 0.00001, false},
|
|
{NaN, -MinValue, 0.00001, false},
|
|
{-MinValue, NaN, 0.00001, false},
|
|
|
|
// Comparisons of numbers on opposite sides of 0
|
|
{1.000000001, -1.0, 0.00001, false},
|
|
{-1.0, 1.000000001, 0.00001, false},
|
|
{-1.000000001, 1.0, 0.00001, false},
|
|
{1.0, -1.000000001, 0.00001, false},
|
|
{10 * MinValue, 10 * -MinValue, 0.00001, true},
|
|
{10000 * MinValue, 10000 * -MinValue, 0.00001, true},
|
|
|
|
// Comparisons of numbers very close to zero
|
|
{MinValue, -MinValue, 0.00001, true},
|
|
{-MinValue, MinValue, 0.00001, true},
|
|
{MinValue, 0, 0.00001, true},
|
|
{0, MinValue, 0.00001, true},
|
|
{-MinValue, 0, 0.00001, true},
|
|
{0, -MinValue, 0.00001, true},
|
|
{0.000000001, -MinValue, 0.00001, false},
|
|
{0.000000001, MinValue, 0.00001, false},
|
|
{MinValue, 0.000000001, 0.00001, false},
|
|
{-MinValue, 0.000000001, 0.00001, false},
|
|
}
|
|
|
|
for _, c := range tests {
|
|
if r := FloatEqualThreshold(c.A, c.B, c.Ep); r != c.Expected {
|
|
t.Errorf("FloatEqualThreshold(%v, %v, %v) != %v (got %v)", c.A, c.B, c.Ep, c.Expected, r)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEqual32(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
a := float32(1.5)
|
|
b := float32(1.0 + .5)
|
|
|
|
if !FloatEqual(a, a) {
|
|
t.Errorf("Float Equal fails on comparing a number with itself")
|
|
}
|
|
|
|
if !FloatEqual(a, b) {
|
|
t.Errorf("Float Equal fails to compare two equivalent numbers with minimal drift")
|
|
} else if !FloatEqual(b, a) {
|
|
t.Errorf("Float Equal is not symmetric for some reason")
|
|
}
|
|
|
|
if !FloatEqual(0.0, 0.0) {
|
|
t.Errorf("Float Equal fails to compare zero values correctly")
|
|
}
|
|
|
|
if FloatEqual(1.5, 1.51) {
|
|
t.Errorf("Float Equal gives false positive on large difference")
|
|
}
|
|
|
|
if FloatEqual(1.5, 0.0) {
|
|
t.Errorf("Float Equal gives false positive comparing with zero")
|
|
}
|
|
}
|
|
|
|
func TestClampf(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if !FloatEqual(Clamp(-1.0, 0.0, 1.0), 0.0) {
|
|
t.Errorf("Clamp returns incorrect value for below threshold")
|
|
}
|
|
|
|
if !FloatEqual(Clamp(0.0, 0.0, 1.0), 0.0) {
|
|
t.Errorf("Clamp does something weird when value is at threshold")
|
|
}
|
|
|
|
if !FloatEqual(Clamp(.14, 0.0, 1.0), .14) {
|
|
t.Errorf("Clamp fails to return correct value when value is within threshold")
|
|
}
|
|
|
|
if !FloatEqual(Clamp(1.1, 0.0, 1.0), 1.0) {
|
|
t.Errorf("Clamp fails to return max threshold when appropriate")
|
|
}
|
|
}
|
|
|
|
func TestIsClamped(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if IsClamped(-1.0, 0.0, 1.0) {
|
|
t.Errorf("Test below min is considered clamped")
|
|
}
|
|
|
|
if !IsClamped(.15, 0.0, 1.0) {
|
|
t.Errorf("Test in threshold returns false")
|
|
}
|
|
|
|
if IsClamped(1.5, 0.0, 1.0) {
|
|
t.Errorf("Test above max threshold returns false positive")
|
|
}
|
|
}
|
|
|
|
/* These benchmarks probably aren't very interesting, there's not really many ways to optimize the functions they're benchmarking */
|
|
|
|
func BenchmarkEqual(b *testing.B) {
|
|
b.StopTimer()
|
|
r := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
f1 := r.Float32()
|
|
f2 := r.Float32()
|
|
b.StartTimer()
|
|
|
|
FloatEqual(f1, f2)
|
|
}
|
|
}
|
|
|
|
// Here just to get a baseline of how much worse the safer equal is
|
|
func BenchmarkBuiltinEqual(b *testing.B) {
|
|
b.StopTimer()
|
|
r := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
f1 := r.Float32()
|
|
f2 := r.Float32()
|
|
b.StartTimer()
|
|
|
|
_ = f1 == f2
|
|
}
|
|
}
|
|
|
|
func BenchmarkClampf(b *testing.B) {
|
|
b.StopTimer()
|
|
r := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
a := r.Float32()
|
|
t1 := r.Float32()
|
|
t2 := r.Float32()
|
|
b.StartTimer()
|
|
|
|
Clamp(a, t1, t2)
|
|
}
|
|
}
|
|
|
|
func TestRound(t *testing.T) {
|
|
tests := []struct {
|
|
Value float32
|
|
Precision int
|
|
Expected float32
|
|
}{
|
|
{0.5, 0, 1},
|
|
{0.123, 2, 0.12},
|
|
{9.99999999, 6, 10},
|
|
{-9.99999999, 6, -10},
|
|
{-0.000099, 4, -0.0001},
|
|
}
|
|
|
|
for _, c := range tests {
|
|
if r := Round(c.Value, c.Precision); r != c.Expected {
|
|
t.Errorf("Round(%v, %v) != %v (got %v)", c.Value, c.Precision, c.Expected, r)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRound(b *testing.B) {
|
|
b.StopTimer()
|
|
r := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
v := r.Float32()
|
|
p := r.Intn(10)
|
|
b.StartTimer()
|
|
|
|
Round(v, p)
|
|
}
|
|
}
|