|
1 | 1 | package tests
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "fmt" |
| 5 | + "math/bits" |
4 | 6 | "math/rand"
|
5 | 7 | "runtime"
|
6 | 8 | "testing"
|
7 | 9 | "testing/quick"
|
| 10 | + |
| 11 | + "github.com/gopherjs/gopherjs/js" |
8 | 12 | )
|
9 | 13 |
|
10 | 14 | // naiveMul64 performs 64-bit multiplication without using the multiplication
|
@@ -93,3 +97,107 @@ func BenchmarkMul64(b *testing.B) {
|
93 | 97 | }
|
94 | 98 | })
|
95 | 99 | }
|
| 100 | + |
| 101 | +func TestIssue733(t *testing.T) { |
| 102 | + if runtime.GOOS != "js" { |
| 103 | + t.Skip("test uses GopherJS-specific features") |
| 104 | + } |
| 105 | + |
| 106 | + t.Run("sign", func(t *testing.T) { |
| 107 | + f := float64(-1) |
| 108 | + i := uint32(f) |
| 109 | + underlying := js.InternalObject(i).Float() // Get the raw JS number behind i. |
| 110 | + if want := float64(4294967295); underlying != want { |
| 111 | + t.Errorf("Got: uint32(float64(%v)) = %v. Want: %v.", f, underlying, want) |
| 112 | + } |
| 113 | + }) |
| 114 | + t.Run("truncation", func(t *testing.T) { |
| 115 | + f := float64(300) |
| 116 | + i := uint8(f) |
| 117 | + underlying := js.InternalObject(i).Float() // Get the raw JS number behind i. |
| 118 | + if want := float64(44); underlying != want { |
| 119 | + t.Errorf("Got: uint32(float64(%v)) = %v. Want: %v.", f, underlying, want) |
| 120 | + } |
| 121 | + }) |
| 122 | +} |
| 123 | + |
| 124 | +// Test_32BitEnvironment tests that GopherJS behaves correctly |
| 125 | +// as a 32-bit environment for integers. To simulate a 32 bit environment |
| 126 | +// we have to use `$imul` instead of `*` to get the correct result. |
| 127 | +func Test_32BitEnvironment(t *testing.T) { |
| 128 | + if bits.UintSize != 32 { |
| 129 | + t.Skip(`test is only relevant for 32-bit environment`) |
| 130 | + } |
| 131 | + |
| 132 | + tests := []struct { |
| 133 | + x, y, exp uint64 |
| 134 | + }{ |
| 135 | + { |
| 136 | + x: 65535, // x = 2^16 - 1 |
| 137 | + y: 65535, // same as x |
| 138 | + exp: 4294836225, // x² works since it doesn't overflow 32 bits. |
| 139 | + }, |
| 140 | + { |
| 141 | + x: 134217729, // x = 2^27 + 1, x < 2^32 and x > sqrt(2^53), so x² overflows 53 bits. |
| 142 | + y: 134217729, // same as x |
| 143 | + exp: 268435457, // x² mod 2^32 = (2^27 + 1)² mod 2^32 = (2^54 + 2^28 + 1) mod 2^32 = 2^28 + 1 |
| 144 | + // In pure JS, `x * x >>> 0`, would result in 268,435,456 because it lost the least significant bit |
| 145 | + // prior to being truncated, where in a real 32 bit environment, it would be 268,435,457 since |
| 146 | + // the rollover removed the most significant bit and doesn't affect the least significant bit. |
| 147 | + }, |
| 148 | + { |
| 149 | + x: 4294967295, // x = 2^32 - 1 another case where x² overflows 53 bits causing a loss of precision. |
| 150 | + y: 4294967295, // same as x |
| 151 | + exp: 1, // x² mod 2^32 = (2^32 - 1)² mod 2^32 = (2^64 - 2^33 + 1) mod 2^32 = 1 |
| 152 | + // In pure JS, `x * x >>> 0`, would result in 0 because it lost the least significant bits. |
| 153 | + }, |
| 154 | + { |
| 155 | + x: 4294967295, // x = 2^32 - 1 |
| 156 | + y: 3221225473, // y = 2^31 + 2^30 + 1 |
| 157 | + exp: 1073741823, // 2^32 - 1. |
| 158 | + // In pure JS, `x * y >>> 0`, would result in 1,073,741,824. |
| 159 | + }, |
| 160 | + { |
| 161 | + x: 4294967295, // x = 2^32 - 1 |
| 162 | + y: 134217729, // y = 2^27 + 1 |
| 163 | + exp: 4160749567, // In pure JS, `x * y >>> 0`, would result in 4,160,749,568. |
| 164 | + }, |
| 165 | + } |
| 166 | + |
| 167 | + for i, test := range tests { |
| 168 | + t.Run(fmt.Sprintf(`#%d/uint32`, i), func(t *testing.T) { |
| 169 | + x, y, exp := uint32(test.x), uint32(test.y), uint32(test.exp) |
| 170 | + if got := x * y; got != exp { |
| 171 | + t.Errorf("got: %d\nwant: %d.", got, exp) |
| 172 | + } |
| 173 | + }) |
| 174 | + |
| 175 | + t.Run(fmt.Sprintf(`#%d/uintptr`, i), func(t *testing.T) { |
| 176 | + x, y, exp := uintptr(test.x), uintptr(test.y), uintptr(test.exp) |
| 177 | + if got := x * y; got != exp { |
| 178 | + t.Errorf("got: %d\nwant: %d.", got, exp) |
| 179 | + } |
| 180 | + }) |
| 181 | + |
| 182 | + t.Run(fmt.Sprintf(`#%d/uint`, i), func(t *testing.T) { |
| 183 | + x, y, exp := uint(test.x), uint(test.y), uint(test.exp) |
| 184 | + if got := x * y; got != exp { |
| 185 | + t.Errorf("got: %d\nwant: %d.", got, exp) |
| 186 | + } |
| 187 | + }) |
| 188 | + |
| 189 | + t.Run(fmt.Sprintf(`#%d/int32`, i), func(t *testing.T) { |
| 190 | + x, y, exp := int32(test.x), int32(test.y), int32(test.exp) |
| 191 | + if got := x * y; got != exp { |
| 192 | + t.Errorf("got: %d\nwant: %d.", got, exp) |
| 193 | + } |
| 194 | + }) |
| 195 | + |
| 196 | + t.Run(fmt.Sprintf(`#%d/int`, i), func(t *testing.T) { |
| 197 | + x, y, exp := int(test.x), int(test.y), int(test.exp) |
| 198 | + if got := x * y; got != exp { |
| 199 | + t.Errorf("got: %d\nwant: %d.", got, exp) |
| 200 | + } |
| 201 | + }) |
| 202 | + } |
| 203 | +} |
0 commit comments