From ac87bf510c14853394f3e38cd6a624700b0c6c69 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:00:28 -0800 Subject: [PATCH 1/7] Add jsnum package, use --- internal/compiler/utilities.go | 15 +-- internal/jsnum/jsnum.go | 72 +++++++++++++ internal/jsnum/jsnum_test.go | 180 +++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 internal/jsnum/jsnum.go create mode 100644 internal/jsnum/jsnum_test.go diff --git a/internal/compiler/utilities.go b/internal/compiler/utilities.go index 7a95f323f5..64cf938537 100644 --- a/internal/compiler/utilities.go +++ b/internal/compiler/utilities.go @@ -11,6 +11,7 @@ import ( "github.com/microsoft/typescript-go/internal/binder" "github.com/microsoft/typescript-go/internal/compiler/diagnostics" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/jsnum" "github.com/microsoft/typescript-go/internal/scanner" "github.com/microsoft/typescript-go/internal/stringutil" ) @@ -2172,7 +2173,7 @@ func createEvaluator(evaluateEntity Evaluator) Evaluator { case ast.KindMinusToken: return evaluatorResult(-value, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) case ast.KindTildeToken: - return evaluatorResult(float64(^int32(value)), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) + return evaluatorResult(jsnum.BitwiseNOT(value), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) } } case ast.KindBinaryExpression: @@ -2187,17 +2188,17 @@ func createEvaluator(evaluateEntity Evaluator) Evaluator { if leftIsNum && rightIsNum { switch operator { case ast.KindBarToken: - return evaluatorResult(float64(int32(leftNum)|int32(rightNum)), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) + return evaluatorResult(jsnum.BitwiseOR(leftNum, rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) case ast.KindAmpersandToken: - return evaluatorResult(float64(int32(leftNum)&int32(rightNum)), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) + return evaluatorResult(jsnum.BitwiseAND(leftNum, rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) case ast.KindGreaterThanGreaterThanToken: - return evaluatorResult(float64(int32(leftNum)>>int32(rightNum)), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) + return evaluatorResult(jsnum.SignedRightShift(leftNum, rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) case ast.KindGreaterThanGreaterThanGreaterThanToken: - return evaluatorResult(float64(uint32(leftNum)>>uint32(rightNum)), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) + return evaluatorResult(jsnum.UnsignedRightShift(leftNum, rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) case ast.KindLessThanLessThanToken: - return evaluatorResult(float64(int32(leftNum)<> toShiftCount(y)) +} + +// https://262.ecma-international.org/#sec-numeric-types-number-unsignedRightShift +func UnsignedRightShift(x, y float64) float64 { + return float64(toUint32(x) >> toShiftCount(y)) +} + +// https://262.ecma-international.org/#sec-numeric-types-number-leftShift +func LeftShift(x, y float64) float64 { + return float64(toInt32(x) << toShiftCount(y)) +} + +// https://262.ecma-international.org/#sec-numeric-types-number-bitwiseNOT +func BitwiseNOT(x float64) float64 { + return float64(^toInt32(x)) +} + +// The below are implemented by https://262.ecma-international.org/#sec-numberbitwiseop. + +// https://262.ecma-international.org/#sec-numeric-types-number-bitwiseOR +func BitwiseOR(x, y float64) float64 { + return float64(toInt32(x) | toInt32(y)) +} + +// https://262.ecma-international.org/#sec-numeric-types-number-bitwiseAND +func BitwiseAND(x, y float64) float64 { + return float64(toInt32(x) & toInt32(y)) +} + +// https://262.ecma-international.org/#sec-numeric-types-number-bitwiseXOR +func BitwiseXOR(x, y float64) float64 { + return float64(toInt32(x) ^ toInt32(y)) +} diff --git a/internal/jsnum/jsnum_test.go b/internal/jsnum/jsnum_test.go new file mode 100644 index 0000000000..c91bbb9006 --- /dev/null +++ b/internal/jsnum/jsnum_test.go @@ -0,0 +1,180 @@ +package jsnum + +import ( + "fmt" + "math" + "testing" + + "gotest.tools/v3/assert" +) + +const ( + maxSafeInteger = 1<<53 - 1 + minSafeInteger = -maxSafeInteger +) + +var toInt32Tests = []struct { + name string + input float64 + want int32 + bench bool +}{ + {"0.0", 0, 0, true}, + {"-0.0", math.Copysign(0, -1), 0, false}, + {"NaN", math.NaN(), 0, true}, + {"+Inf", math.Inf(1), 0, true}, + {"-Inf", math.Inf(-1), 0, true}, + {"MaxInt32", float64(math.MaxInt32), math.MaxInt32, false}, + {"MaxInt32+1", float64(int64(math.MaxInt32) + 1), math.MinInt32, true}, + {"MinInt32", float64(math.MinInt32), math.MinInt32, false}, + {"MinInt32-1", float64(int64(math.MinInt32) - 1), math.MaxInt32, true}, + {"MIN_SAFE_INTEGER", minSafeInteger, 1, false}, + {"MIN_SAFE_INTEGER-1", minSafeInteger - 1, 0, false}, + {"MIN_SAFE_INTEGER+1", minSafeInteger + 1, 2, false}, + {"MAX_SAFE_INTEGER", maxSafeInteger, -1, true}, + {"MAX_SAFE_INTEGER-1", maxSafeInteger - 1, -2, true}, + {"MAX_SAFE_INTEGER+1", maxSafeInteger + 1, 0, true}, + {"-8589934590", -8589934590, 2, false}, + {"0xDEADBEEF", 0xDEADBEEF, -559038737, true}, + {"4294967808", 4294967808, 512, false}, + {"-0.4", -0.4, 0, false}, + {"SmallestNonzeroFloat64", math.SmallestNonzeroFloat64, 0, false}, + {"-SmallestNonzeroFloat64", -math.SmallestNonzeroFloat64, 0, false}, + {"MaxFloat64", math.MaxFloat64, 0, false}, + {"-MaxFloat64", -math.MaxFloat64, 0, false}, + {"Largest subnormal number", math.Float64frombits(0x000FFFFFFFFFFFFF), 0, false}, + {"Smallest positive normal number", math.Float64frombits(0x0010000000000000), 0, false}, + {"Largest normal number", math.MaxFloat64, 0, false}, + {"-Largest normal number", -math.MaxFloat64, 0, false}, + {"1.0", 1.0, 1, false}, + {"-1.0", -1.0, -1, false}, + {"1e308", 1e308, 0, false}, + {"-1e308", -1e308, 0, false}, + {"math.Pi", math.Pi, 3, false}, + {"-math.Pi", -math.Pi, -3, false}, + {"math.E", math.E, 2, false}, + {"-math.E", -math.E, -2, false}, + {"0.5", 0.5, 0, false}, + {"-0.5", -0.5, 0, false}, + {"0.49999999999999994", 0.49999999999999994, 0, false}, + {"-0.49999999999999994", -0.49999999999999994, 0, false}, + {"0.5000000000000001", 0.5000000000000001, 0, false}, + {"-0.5000000000000001", -0.5000000000000001, 0, false}, + {"2^31 + 0.5", 2147483648.5, -2147483648, false}, + {"-2^31 - 0.5", -2147483648.5, -2147483648, false}, + {"2^40", 1099511627776, 0, false}, + {"-2^40", -1099511627776, 0, false}, + {"TypeFlagsNarrowable", 536624127, 536624127, true}, +} + +func TestToInt32(t *testing.T) { + t.Parallel() + + for _, test := range toInt32Tests { + t.Run(fmt.Sprintf("%s (%v)", test.name, test.input), func(t *testing.T) { + t.Parallel() + assert.Equal(t, toInt32(test.input), test.want) + }) + } +} + +var sink int32 + +func BenchmarkToInt32(b *testing.B) { + for _, test := range toInt32Tests { + if !test.bench { + continue + } + + b.Run(fmt.Sprintf("%s (%v)", test.name, test.input), func(b *testing.B) { + for range b.N { + sink = toInt32(test.input) + } + }) + } +} + +func TestBitwiseNOT(t *testing.T) { + t.Parallel() + + assert.Equal(t, BitwiseNOT(-2147483649), BitwiseNOT(2147483647)) + assert.Equal(t, BitwiseNOT(-4294967296), BitwiseNOT(0)) + assert.Equal(t, BitwiseNOT(-2147483648), BitwiseNOT(-2147483648)) + assert.Equal(t, BitwiseNOT(-4294967296), BitwiseNOT(0)) +} + +func TestBitwiseAND(t *testing.T) { + t.Parallel() + + assert.Equal(t, BitwiseAND(1, 0), 0.0) + assert.Equal(t, BitwiseAND(1, 1), 1.0) +} + +func TestBitwiseOR(t *testing.T) { + t.Parallel() + + assert.Equal(t, BitwiseOR(1, 0), 1.0) + assert.Equal(t, BitwiseOR(1, 1), 1.0) +} + +func TestBitwiseXOR(t *testing.T) { + t.Parallel() + + assert.Equal(t, BitwiseXOR(1, 0), 1.0) + assert.Equal(t, BitwiseXOR(1, 1), 0.0) +} + +func TestSignedRightShift(t *testing.T) { + t.Parallel() + + assert.Equal(t, SignedRightShift(1, 0), 1.0) + assert.Equal(t, SignedRightShift(1, 1), 0.0) + assert.Equal(t, SignedRightShift(1, 2), 0.0) + assert.Equal(t, SignedRightShift(1, 31), 0.0) + assert.Equal(t, SignedRightShift(1, 32), 1.0) + + assert.Equal(t, SignedRightShift(-4, 0), -4.0) + assert.Equal(t, SignedRightShift(-4, 1), -2.0) + assert.Equal(t, SignedRightShift(-4, 2), -1.0) + assert.Equal(t, SignedRightShift(-4, 3), -1.0) + assert.Equal(t, SignedRightShift(-4, 4), -1.0) + assert.Equal(t, SignedRightShift(-4, 31), -1.0) + assert.Equal(t, SignedRightShift(-4, 32), -4.0) + assert.Equal(t, SignedRightShift(-4, 33), -2.0) +} + +func TestUnsignedRightShift(t *testing.T) { + t.Parallel() + + assert.Equal(t, UnsignedRightShift(1, 0), 1.0) + assert.Equal(t, UnsignedRightShift(1, 1), 0.0) + assert.Equal(t, UnsignedRightShift(1, 2), 0.0) + assert.Equal(t, UnsignedRightShift(1, 31), 0.0) + assert.Equal(t, UnsignedRightShift(1, 32), 1.0) + + assert.Equal(t, UnsignedRightShift(-4, 0), 4294967292.0) + assert.Equal(t, UnsignedRightShift(-4, 1), 2147483646.0) + assert.Equal(t, UnsignedRightShift(-4, 2), 1073741823.0) + assert.Equal(t, UnsignedRightShift(-4, 3), 536870911.0) + assert.Equal(t, UnsignedRightShift(-4, 4), 268435455.0) + assert.Equal(t, UnsignedRightShift(-4, 31), 1.0) + assert.Equal(t, UnsignedRightShift(-4, 32), 4294967292.0) + assert.Equal(t, UnsignedRightShift(-4, 33), 2147483646.0) +} + +func TestLeftShift(t *testing.T) { + t.Parallel() + + assert.Equal(t, LeftShift(1, 0), 1.0) + assert.Equal(t, LeftShift(1, 1), 2.0) + assert.Equal(t, LeftShift(1, 2), 4.0) + assert.Equal(t, LeftShift(1, 31), -2147483648.0) + assert.Equal(t, LeftShift(1, 32), 1.0) + + assert.Equal(t, LeftShift(-4, 0), -4.0) + assert.Equal(t, LeftShift(-4, 1), -8.0) + assert.Equal(t, LeftShift(-4, 2), -16.0) + assert.Equal(t, LeftShift(-4, 3), -32.0) + assert.Equal(t, LeftShift(-4, 31), 0.0) + assert.Equal(t, LeftShift(-4, 32), -4.0) +} From 1c28c9ad7c5948e350726407c0c42a350154ca4e Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:39:45 -0800 Subject: [PATCH 2/7] Use bitwise ops for non-finite check --- internal/jsnum/jsnum.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index 456b7b583d..fd02a8cabd 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -11,8 +11,10 @@ func toUint32(x float64) uint32 { return uint32(smi) } - // If the number is non-finite, it maps to zero. - if math.IsNaN(x) || math.IsInf(x, 0) { + // If the number is non-finite (NaN, +Inf, -Inf; exp=0x7FF), it maps to zero. + // This is equivalent to checking `math.IsNaN(x) || math.IsInf(x, 0)` in one operation. + const mask = 0x7FF0000000000000 + if math.Float64bits(x)&mask == mask { return 0 } From 9a38c668d0114ba3fd36157eda28f35badbe5b7f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:42:43 -0800 Subject: [PATCH 3/7] Don't use math.Pow directly --- internal/compiler/utilities.go | 2 +- internal/jsnum/jsnum.go | 13 +++++++++++ internal/jsnum/jsnum_test.go | 40 +++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/internal/compiler/utilities.go b/internal/compiler/utilities.go index 64cf938537..81fd754b94 100644 --- a/internal/compiler/utilities.go +++ b/internal/compiler/utilities.go @@ -2210,7 +2210,7 @@ func createEvaluator(evaluateEntity Evaluator) Evaluator { case ast.KindPercentToken: return evaluatorResult(leftNum-rightNum*math.Floor(leftNum/rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) case ast.KindAsteriskAsteriskToken: - return evaluatorResult(math.Pow(leftNum, rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) + return evaluatorResult(jsnum.Exponentiate(leftNum, rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) } } leftStr, leftIsStr := left.value.(string) diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index fd02a8cabd..3c2642e652 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -72,3 +72,16 @@ func BitwiseAND(x, y float64) float64 { func BitwiseXOR(x, y float64) float64 { return float64(toInt32(x) ^ toInt32(y)) } + +// https://262.ecma-international.org/#sec-numeric-types-number-exponentiate +func Exponentiate(base, exponent float64) float64 { + // Special cases in https://262.ecma-international.org/#sec-numeric-types-number-exponentiate + if (base == 1 || base == -1) && math.IsInf(exponent, 0) { + return math.NaN() + } + if base == 1 && math.IsNaN(exponent) { + return math.NaN() + } + + return math.Pow(base, exponent) +} diff --git a/internal/jsnum/jsnum_test.go b/internal/jsnum/jsnum_test.go index c91bbb9006..cc259c2248 100644 --- a/internal/jsnum/jsnum_test.go +++ b/internal/jsnum/jsnum_test.go @@ -13,6 +13,8 @@ const ( minSafeInteger = -maxSafeInteger ) +var negativeZero = math.Copysign(0, -1) + var toInt32Tests = []struct { name string input float64 @@ -20,7 +22,7 @@ var toInt32Tests = []struct { bench bool }{ {"0.0", 0, 0, true}, - {"-0.0", math.Copysign(0, -1), 0, false}, + {"-0.0", negativeZero, 0, false}, {"NaN", math.NaN(), 0, true}, {"+Inf", math.Inf(1), 0, true}, {"-Inf", math.Inf(-1), 0, true}, @@ -178,3 +180,39 @@ func TestLeftShift(t *testing.T) { assert.Equal(t, LeftShift(-4, 31), 0.0) assert.Equal(t, LeftShift(-4, 32), -4.0) } + +func TestExponentiate(t *testing.T) { + t.Parallel() + + assert.Equal(t, Exponentiate(2, 3), 8.0) + + assert.Equal(t, Exponentiate(math.Inf(1), 3), math.Inf(1)) + assert.Equal(t, Exponentiate(math.Inf(1), -5), 0.0) + + assert.Equal(t, Exponentiate(math.Inf(-1), 3), math.Inf(-1)) + assert.Equal(t, Exponentiate(math.Inf(-1), 4), math.Inf(1)) + assert.Equal(t, Exponentiate(math.Inf(-1), -3), negativeZero) + assert.Equal(t, Exponentiate(math.Inf(-1), -4), 0.0) + + assert.Equal(t, Exponentiate(0, 3), 0.0) + assert.Equal(t, Exponentiate(0, -10), math.Inf(1)) + + assert.Equal(t, Exponentiate(negativeZero, 3), negativeZero) + assert.Equal(t, Exponentiate(negativeZero, 4), 0.0) + assert.Equal(t, Exponentiate(negativeZero, -3), math.Inf(-1)) + assert.Equal(t, Exponentiate(negativeZero, -4), math.Inf(1)) + + assert.Equal(t, Exponentiate(3, math.Inf(1)), math.Inf(1)) + assert.Equal(t, Exponentiate(-3, math.Inf(1)), 0.0) + + assert.Equal(t, Exponentiate(3, math.Inf(-1)), 0.0) + assert.Equal(t, Exponentiate(-3, math.Inf(-1)), math.Inf(1)) + + // Special cases in https://262.ecma-international.org/#sec-numeric-types-number-exponentiate + assert.Assert(t, math.IsNaN(Exponentiate(math.NaN(), 3))) + assert.Assert(t, math.IsNaN(Exponentiate(1, math.Inf(1)))) + assert.Assert(t, math.IsNaN(Exponentiate(1, math.Inf(-1)))) + assert.Assert(t, math.IsNaN(Exponentiate(-1, math.Inf(1)))) + assert.Assert(t, math.IsNaN(Exponentiate(-1, math.Inf(-1)))) + assert.Assert(t, math.IsNaN(Exponentiate(1, math.NaN()))) +} From 504b52fadf746304ebd0676b320d9330a0202a84 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:57:39 -0800 Subject: [PATCH 4/7] Fix remainder --- internal/compiler/utilities.go | 2 +- internal/jsnum/jsnum.go | 32 ++++++++++++++++++++++++++++++++ internal/jsnum/jsnum_test.go | 21 +++++++++++++++++++-- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/internal/compiler/utilities.go b/internal/compiler/utilities.go index 81fd754b94..d5bf949a4e 100644 --- a/internal/compiler/utilities.go +++ b/internal/compiler/utilities.go @@ -2208,7 +2208,7 @@ func createEvaluator(evaluateEntity Evaluator) Evaluator { case ast.KindMinusToken: return evaluatorResult(leftNum-rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) case ast.KindPercentToken: - return evaluatorResult(leftNum-rightNum*math.Floor(leftNum/rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) + return evaluatorResult(jsnum.Remainder(leftNum, rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) case ast.KindAsteriskAsteriskToken: return evaluatorResult(jsnum.Exponentiate(leftNum, rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences) } diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index 3c2642e652..4d908202f3 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -73,6 +73,38 @@ func BitwiseXOR(x, y float64) float64 { return float64(toInt32(x) ^ toInt32(y)) } +var negativeZero = math.Copysign(0, -1) + +// https://262.ecma-international.org/#sec-numeric-types-number-remainder +func Remainder(n, d float64) float64 { + if math.IsNaN(n) || math.IsNaN(d) { + return math.NaN() + } + + if math.IsInf(n, 0) { + return math.NaN() + } + + if math.IsInf(d, 0) { + return n + } + + if d == 0 { + return math.NaN() + } + + if n == 0 { + return n + } + + r := n - d*math.Trunc(n/d) + if r == 0 || n < 0 { + return negativeZero + } + + return r +} + // https://262.ecma-international.org/#sec-numeric-types-number-exponentiate func Exponentiate(base, exponent float64) float64 { // Special cases in https://262.ecma-international.org/#sec-numeric-types-number-exponentiate diff --git a/internal/jsnum/jsnum_test.go b/internal/jsnum/jsnum_test.go index cc259c2248..d1d3f05a86 100644 --- a/internal/jsnum/jsnum_test.go +++ b/internal/jsnum/jsnum_test.go @@ -13,8 +13,6 @@ const ( minSafeInteger = -maxSafeInteger ) -var negativeZero = math.Copysign(0, -1) - var toInt32Tests = []struct { name string input float64 @@ -181,6 +179,25 @@ func TestLeftShift(t *testing.T) { assert.Equal(t, LeftShift(-4, 32), -4.0) } +func TestRemainder(t *testing.T) { + t.Parallel() + + assert.Assert(t, math.IsNaN(Remainder(math.NaN(), 1))) + assert.Assert(t, math.IsNaN(Remainder(1, math.NaN()))) + + assert.Assert(t, math.IsNaN(Remainder(math.Inf(1), 1))) + assert.Assert(t, math.IsNaN(Remainder(math.Inf(-1), 1))) + + assert.Equal(t, Remainder(123, math.Inf(1)), 123.0) + assert.Equal(t, Remainder(123, math.Inf(-1)), 123.0) + + assert.Assert(t, math.IsNaN(Remainder(123, 0))) + assert.Assert(t, math.IsNaN(Remainder(123, negativeZero))) + + assert.Equal(t, Remainder(0, 123), 0.0) + assert.Equal(t, Remainder(negativeZero, 123), negativeZero) +} + func TestExponentiate(t *testing.T) { t.Parallel() From 98f242ccd9e6441c7d04a7448e4d497530bc65c7 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:58:22 -0800 Subject: [PATCH 5/7] Switch --- internal/jsnum/jsnum.go | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index 4d908202f3..63c24a8c3e 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -77,23 +77,16 @@ var negativeZero = math.Copysign(0, -1) // https://262.ecma-international.org/#sec-numeric-types-number-remainder func Remainder(n, d float64) float64 { - if math.IsNaN(n) || math.IsNaN(d) { + switch { + case math.IsNaN(n) || math.IsNaN(d): return math.NaN() - } - - if math.IsInf(n, 0) { + case math.IsInf(n, 0): return math.NaN() - } - - if math.IsInf(d, 0) { + case math.IsInf(d, 0): return n - } - - if d == 0 { + case d == 0: return math.NaN() - } - - if n == 0 { + case n == 0: return n } @@ -108,10 +101,10 @@ func Remainder(n, d float64) float64 { // https://262.ecma-international.org/#sec-numeric-types-number-exponentiate func Exponentiate(base, exponent float64) float64 { // Special cases in https://262.ecma-international.org/#sec-numeric-types-number-exponentiate - if (base == 1 || base == -1) && math.IsInf(exponent, 0) { + switch { + case (base == 1 || base == -1) && math.IsInf(exponent, 0): return math.NaN() - } - if base == 1 && math.IsNaN(exponent) { + case base == 1 && math.IsNaN(exponent): return math.NaN() } From d7b6901a23b33e1902003333c78488acfff01ffe Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:03:47 -0800 Subject: [PATCH 6/7] Switch links to single pages --- internal/jsnum/jsnum.go | 25 ++++++++++++------------- internal/jsnum/jsnum_test.go | 1 - 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/internal/jsnum/jsnum.go b/internal/jsnum/jsnum.go index 63c24a8c3e..3cc22adff9 100644 --- a/internal/jsnum/jsnum.go +++ b/internal/jsnum/jsnum.go @@ -3,7 +3,7 @@ package jsnum import "math" -// https://262.ecma-international.org/#sec-touint32 +// https://tc39.es/ecma262/2024/multipage/abstract-operations.html#sec-touint32 func toUint32(x float64) uint32 { // 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. @@ -26,7 +26,7 @@ func toUint32(x float64) uint32 { return uint32(x) } -// https://262.ecma-international.org/#sec-toint32 +// https://tc39.es/ecma262/2024/multipage/abstract-operations.html#sec-toint32 func toInt32(x float64) int32 { // The only difference between ToUint32 and ToInt32 is the interpretation of the bits. return int32(toUint32(x)) @@ -36,46 +36,46 @@ func toShiftCount(x float64) uint32 { return toUint32(x) & 31 } -// https://262.ecma-international.org/#sec-numeric-types-number-signedRightShift +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-signedRightShift func SignedRightShift(x, y float64) float64 { return float64(toInt32(x) >> toShiftCount(y)) } -// https://262.ecma-international.org/#sec-numeric-types-number-unsignedRightShift +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-unsignedRightShift func UnsignedRightShift(x, y float64) float64 { return float64(toUint32(x) >> toShiftCount(y)) } -// https://262.ecma-international.org/#sec-numeric-types-number-leftShift +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-leftShift func LeftShift(x, y float64) float64 { return float64(toInt32(x) << toShiftCount(y)) } -// https://262.ecma-international.org/#sec-numeric-types-number-bitwiseNOT +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-bitwiseNOT func BitwiseNOT(x float64) float64 { return float64(^toInt32(x)) } -// The below are implemented by https://262.ecma-international.org/#sec-numberbitwiseop. +// The below are implemented by https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numberbitwiseop. -// https://262.ecma-international.org/#sec-numeric-types-number-bitwiseOR +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-bitwiseOR func BitwiseOR(x, y float64) float64 { return float64(toInt32(x) | toInt32(y)) } -// https://262.ecma-international.org/#sec-numeric-types-number-bitwiseAND +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-bitwiseAND func BitwiseAND(x, y float64) float64 { return float64(toInt32(x) & toInt32(y)) } -// https://262.ecma-international.org/#sec-numeric-types-number-bitwiseXOR +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-bitwiseXOR func BitwiseXOR(x, y float64) float64 { return float64(toInt32(x) ^ toInt32(y)) } var negativeZero = math.Copysign(0, -1) -// https://262.ecma-international.org/#sec-numeric-types-number-remainder +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-remainder func Remainder(n, d float64) float64 { switch { case math.IsNaN(n) || math.IsNaN(d): @@ -98,9 +98,8 @@ func Remainder(n, d float64) float64 { return r } -// https://262.ecma-international.org/#sec-numeric-types-number-exponentiate +// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate func Exponentiate(base, exponent float64) float64 { - // Special cases in https://262.ecma-international.org/#sec-numeric-types-number-exponentiate switch { case (base == 1 || base == -1) && math.IsInf(exponent, 0): return math.NaN() diff --git a/internal/jsnum/jsnum_test.go b/internal/jsnum/jsnum_test.go index d1d3f05a86..727360289e 100644 --- a/internal/jsnum/jsnum_test.go +++ b/internal/jsnum/jsnum_test.go @@ -225,7 +225,6 @@ func TestExponentiate(t *testing.T) { assert.Equal(t, Exponentiate(3, math.Inf(-1)), 0.0) assert.Equal(t, Exponentiate(-3, math.Inf(-1)), math.Inf(1)) - // Special cases in https://262.ecma-international.org/#sec-numeric-types-number-exponentiate assert.Assert(t, math.IsNaN(Exponentiate(math.NaN(), 3))) assert.Assert(t, math.IsNaN(Exponentiate(1, math.Inf(1)))) assert.Assert(t, math.IsNaN(Exponentiate(1, math.Inf(-1)))) From 41d3c48a043f50edcc0132b99d05ce35f2376ef1 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:48:39 -0800 Subject: [PATCH 7/7] Fix incorrect test --- internal/jsnum/jsnum_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/jsnum/jsnum_test.go b/internal/jsnum/jsnum_test.go index 727360289e..574e45ca0a 100644 --- a/internal/jsnum/jsnum_test.go +++ b/internal/jsnum/jsnum_test.go @@ -220,10 +220,10 @@ func TestExponentiate(t *testing.T) { assert.Equal(t, Exponentiate(negativeZero, -4), math.Inf(1)) assert.Equal(t, Exponentiate(3, math.Inf(1)), math.Inf(1)) - assert.Equal(t, Exponentiate(-3, math.Inf(1)), 0.0) + assert.Equal(t, Exponentiate(-3, math.Inf(1)), math.Inf(1)) assert.Equal(t, Exponentiate(3, math.Inf(-1)), 0.0) - assert.Equal(t, Exponentiate(-3, math.Inf(-1)), math.Inf(1)) + assert.Equal(t, Exponentiate(-3, math.Inf(-1)), 0.0) assert.Assert(t, math.IsNaN(Exponentiate(math.NaN(), 3))) assert.Assert(t, math.IsNaN(Exponentiate(1, math.Inf(1))))