diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp index eb8416fadd..506113bbb4 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.cpp +++ b/Userland/Libraries/LibJS/Runtime/Value.cpp @@ -1212,11 +1212,36 @@ Value mod(GlobalObject& global_object, Value lhs, Value rhs) if (vm.exception()) return {}; if (both_number(lhs_numeric, rhs_numeric)) { + // 6.1.6.1.6 Number::remainder ( n, d ), https://tc39.es/ecma262/#sec-numeric-types-number-remainder + + // 1. If n is NaN or d is NaN, return NaN. if (lhs_numeric.is_nan() || rhs_numeric.is_nan()) return js_nan(); + + // 2. If n is +∞𝔽 or n is -∞𝔽, return NaN. + if (lhs_numeric.is_positive_infinity() || lhs_numeric.is_negative_infinity()) + return js_nan(); + + // 3. If d is +∞𝔽 or d is -∞𝔽, return n. + if (rhs_numeric.is_positive_infinity() || rhs_numeric.is_negative_infinity()) + return lhs_numeric; + + // 4. If d is +0𝔽 or d is -0𝔽, return NaN. + if (rhs_numeric.is_positive_zero() || rhs_numeric.is_negative_zero()) + return js_nan(); + + // 5. If n is +0𝔽 or n is -0𝔽, return n. + if (lhs_numeric.is_positive_zero() || lhs_numeric.is_negative_zero()) + return lhs_numeric; + + // 6. Assert: n and d are finite and non-zero. + auto index = lhs_numeric.as_double(); auto period = rhs_numeric.as_double(); auto trunc = (double)(i32)(index / period); + + // 7. Let r be ℝ(n) - (ℝ(d) × q) where q is an integer that is negative if and only if n and d have opposite sign, and whose magnitude is as large as possible without exceeding the magnitude of ℝ(n) / ℝ(d). + // 8. Return 𝔽(r). return Value(index - trunc * period); } if (both_bigint(lhs_numeric, rhs_numeric)) { diff --git a/Userland/Libraries/LibJS/Tests/operators/modulo-basic.js b/Userland/Libraries/LibJS/Tests/operators/modulo-basic.js index 22b60ea256..c364feaddb 100644 --- a/Userland/Libraries/LibJS/Tests/operators/modulo-basic.js +++ b/Userland/Libraries/LibJS/Tests/operators/modulo-basic.js @@ -13,4 +13,26 @@ test("basic functionality", () => { expect(-4 % 2).toBe(-0); expect(5.5 % 2).toBe(1.5); expect(NaN % 2).toBeNaN(); + expect(2 % NaN).toBeNaN(); + expect(NaN % NaN).toBeNaN(); + expect(Infinity % 1).toBeNaN(); + expect(-Infinity % 1).toBeNaN(); + expect(1 % Infinity).toBe(1); + expect(1 % -Infinity).toBe(1); + expect(1 % 0).toBeNaN(); + expect(1 % -0).toBeNaN(); + expect(0 % 5).toBe(0); + expect(-0 % 5).toBe(-0); + + // test262 examples + expect(1 % null).toBeNaN(); + expect(null % 1).toBe(0); + expect(true % null).toBeNaN(); + expect(null % true).toBe(0); + expect("1" % null).toBeNaN(); + expect(null % "1").toBe(0); + expect(null % undefined).toBeNaN(); + expect(undefined % null).toBeNaN(); + expect(undefined % undefined).toBeNaN(); + expect(null % null).toBeNaN(); });