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  }