github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/tests/numeric_test.go (about) 1 package tests 2 3 import ( 4 "math/rand" 5 "runtime" 6 "testing" 7 "testing/quick" 8 9 "github.com/gopherjs/gopherjs/js" 10 ) 11 12 // naiveMul64 performs 64-bit multiplication without using the multiplication 13 // operation and can be used to test correctness of the compiler's multiplication 14 // implementation. 15 func naiveMul64(x, y uint64) uint64 { 16 var z uint64 = 0 17 for i := 0; i < 64; i++ { 18 mask := uint64(1) << i 19 if y&mask > 0 { 20 z += x << i 21 } 22 } 23 return z 24 } 25 26 func TestMul64(t *testing.T) { 27 cfg := &quick.Config{ 28 MaxCountScale: 10000, 29 Rand: rand.New(rand.NewSource(0x5EED)), // Fixed seed for reproducibility. 30 } 31 if testing.Short() { 32 cfg.MaxCountScale = 1000 33 } 34 35 t.Run("unsigned", func(t *testing.T) { 36 err := quick.CheckEqual( 37 func(x, y uint64) uint64 { return x * y }, 38 naiveMul64, 39 cfg) 40 if err != nil { 41 t.Error(err) 42 } 43 }) 44 t.Run("signed", func(t *testing.T) { 45 // GopherJS represents 64-bit signed integers in a two-complement form, 46 // so bitwise multiplication looks identical for signed and unsigned integers 47 // and we can reuse naiveMul64() as a reference implementation for both with 48 // appropriate type conversions. 49 err := quick.CheckEqual( 50 func(x, y int64) int64 { return x * y }, 51 func(x, y int64) int64 { return int64(naiveMul64(uint64(x), uint64(y))) }, 52 cfg) 53 if err != nil { 54 t.Error(err) 55 } 56 }) 57 } 58 59 func BenchmarkMul64(b *testing.B) { 60 // Prepare a randomized set of multipliers to make sure the benchmark doesn't 61 // get too specific for a single value. The trade-off is that the cost of 62 // loading from an array gets mixed into the result, but it is good enough for 63 // relative comparisons. 64 r := rand.New(rand.NewSource(0x5EED)) 65 const size = 1024 66 xU := [size]uint64{} 67 yU := [size]uint64{} 68 xS := [size]int64{} 69 yS := [size]int64{} 70 for i := 0; i < size; i++ { 71 xU[i] = r.Uint64() 72 yU[i] = r.Uint64() 73 xS[i] = r.Int63() | (r.Int63n(2) << 63) 74 yS[i] = r.Int63() | (r.Int63n(2) << 63) 75 } 76 77 b.Run("noop", func(b *testing.B) { 78 // This benchmark allows to gauge the cost of array load operations without 79 // the multiplications. 80 for i := 0; i < b.N; i++ { 81 runtime.KeepAlive(yU[i%size]) 82 runtime.KeepAlive(xU[i%size]) 83 } 84 }) 85 b.Run("unsigned", func(b *testing.B) { 86 for i := 0; i < b.N; i++ { 87 z := xU[i%size] * yU[i%size] 88 runtime.KeepAlive(z) 89 } 90 }) 91 b.Run("signed", func(b *testing.B) { 92 for i := 0; i < b.N; i++ { 93 z := xS[i%size] * yS[i%size] 94 runtime.KeepAlive(z) 95 } 96 }) 97 } 98 99 func TestIssue733(t *testing.T) { 100 if runtime.GOOS != "js" { 101 t.Skip("test uses GopherJS-specific features") 102 } 103 104 t.Run("sign", func(t *testing.T) { 105 f := float64(-1) 106 i := uint32(f) 107 underlying := js.InternalObject(i).Float() // Get the raw JS number behind i. 108 if want := float64(4294967295); underlying != want { 109 t.Errorf("Got: uint32(float64(%v)) = %v. Want: %v.", f, underlying, want) 110 } 111 }) 112 t.Run("truncation", func(t *testing.T) { 113 f := float64(300) 114 i := uint8(f) 115 underlying := js.InternalObject(i).Float() // Get the raw JS number behind i. 116 if want := float64(44); underlying != want { 117 t.Errorf("Got: uint32(float64(%v)) = %v. Want: %v.", f, underlying, want) 118 } 119 }) 120 }