From a8632524fca05c0289b3f10d91a654cbbbe4c0a7 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 9 Oct 2025 15:45:29 -0700 Subject: [PATCH 1/5] clearer test --- internal/jsnum/jsnum_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/jsnum/jsnum_test.go b/internal/jsnum/jsnum_test.go index 6036d0be9d..53c358204d 100644 --- a/internal/jsnum/jsnum_test.go +++ b/internal/jsnum/jsnum_test.go @@ -109,18 +109,18 @@ func TestBitwiseNOT(t *testing.T) { t.Parallel() tests := []struct { - input, want Number + got, want Number }{ - {-2147483649, Number(2147483647).BitwiseNOT()}, - {-4294967296, Number(0).BitwiseNOT()}, - {2147483648, Number(-2147483648).BitwiseNOT()}, - {4294967296, Number(0).BitwiseNOT()}, + {Number(-2147483649).BitwiseNOT(), Number(2147483647).BitwiseNOT()}, + {Number(-4294967296).BitwiseNOT(), Number(0).BitwiseNOT()}, + {Number(2147483648).BitwiseNOT(), Number(-2147483648).BitwiseNOT()}, + {Number(4294967296).BitwiseNOT(), Number(0).BitwiseNOT()}, } for _, test := range tests { - t.Run(test.input.String(), func(t *testing.T) { + t.Run(test.got.String(), func(t *testing.T) { t.Parallel() - assertEqualNumber(t, test.input.BitwiseNOT(), test.want) + assertEqualNumber(t, test.got, test.want) }) } } From 2b3acdf902bccf69efcb2ea78c9c18767dd1f289 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 9 Oct 2025 15:27:32 -0700 Subject: [PATCH 2/5] Change implementation again --- internal/jsnum/jsnum.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index 14d8338a08..68a26e26b1 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -41,12 +41,18 @@ func isNonFinite(x float64) bool { } // https://tc39.es/ecma262/2024/multipage/abstract-operations.html#sec-touint32 -func (n Number) toUint32() uint32 { +func (x Number) toUint32() uint32 { + // The only difference between ToUint32 and ToInt32 is the interpretation of the bits. + return uint32(x.toInt32()) +} + +// https://tc39.es/ecma262/2024/multipage/abstract-operations.html#sec-toint32 +func (n Number) toInt32() int32 { x := float64(n) // Fast path: if the number is the range (-2^31, 2^32), i.e. an SMI, // then we don't need to do any special mapping. if smi := int32(x); float64(smi) == x { - return uint32(smi) + return smi } // If the number is non-finite (NaN, +Inf, -Inf; exp=0x7FF), it maps to zero. @@ -56,16 +62,12 @@ func (n Number) toUint32() uint32 { // Otherwise, take x modulo 2^32, mapping positive numbers // to [0, 2^32) and negative numbers to (-2^32, -0.0]. - x = math.Mod(x, 1<<32) - // Convert to uint32, which will wrap negative numbers. - return uint32(x) -} - -// https://tc39.es/ecma262/2024/multipage/abstract-operations.html#sec-toint32 -func (x Number) toInt32() int32 { - // The only difference between ToUint32 and ToInt32 is the interpretation of the bits. - return int32(x.toUint32()) + i := int32(uint32(math.Mod(math.Abs(x), 1<<32))) + if math.Signbit(x) { + return -i + } + return i } func (x Number) toShiftCount() uint32 { From 6252fc66b2e5ff9cdafe38d628309ba9f7a9d0c3 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 9 Oct 2025 16:04:07 -0700 Subject: [PATCH 3/5] Futher spec like --- internal/jsnum/jsnum.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index 68a26e26b1..323647a24e 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -49,25 +49,24 @@ func (x Number) toUint32() uint32 { // https://tc39.es/ecma262/2024/multipage/abstract-operations.html#sec-toint32 func (n Number) toInt32() int32 { x := float64(n) + // Fast path: if the number is the range (-2^31, 2^32), i.e. an SMI, // then we don't need to do any special mapping. if smi := int32(x); float64(smi) == x { return smi } - // If the number is non-finite (NaN, +Inf, -Inf; exp=0x7FF), it maps to zero. - if isNonFinite(x) { + // 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽. + if x == 0 || isNonFinite(x) { return 0 } - // Otherwise, take x modulo 2^32, mapping positive numbers - // to [0, 2^32) and negative numbers to (-2^32, -0.0]. - - i := int32(uint32(math.Mod(math.Abs(x), 1<<32))) - if math.Signbit(x) { - return -i - } - return i + // Let int be truncate(ℝ(number)). + x = math.Trunc(x) + // Let int32bit be int modulo 2**32. + x = math.Mod(x, 1<<32) + // If int32bit ≥ 2**31, return 𝔽(int32bit - 2**32); otherwise return 𝔽(int32bit). + return int32(int64(x)) } func (x Number) toShiftCount() uint32 { From 96d5404926531f9b2683766af81fb14da30b87d0 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 9 Oct 2025 16:18:49 -0700 Subject: [PATCH 4/5] zero case --- internal/jsnum/jsnum.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index 323647a24e..b57d86b382 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -57,7 +57,8 @@ func (n Number) toInt32() int32 { } // 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽. - if x == 0 || isNonFinite(x) { + // Zero was covered by the test above. + if isNonFinite(x) { return 0 } From 8896ea204b6b31f9de92ecc8242c9623df95113e Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 9 Oct 2025 16:26:46 -0700 Subject: [PATCH 5/5] Update internal/jsnum/jsnum.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- internal/jsnum/jsnum.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index b57d86b382..9e54baaf72 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -50,7 +50,7 @@ func (x Number) toUint32() uint32 { func (n Number) toInt32() int32 { x := float64(n) - // Fast path: if the number is the range (-2^31, 2^32), i.e. an SMI, + // Fast path: if the number is in the range (-2^31, 2^32), i.e. an SMI, // then we don't need to do any special mapping. if smi := int32(x); float64(smi) == x { return smi