Przeglądaj źródła

LibJS: Implement the Number::remainder AO using fmod

The ECMA verbiage for modulus is the mathematical definition implemented
by fmod, so let's just use that rather than trying to reimplement all
the edge cases.
Anonymous 3 lat temu
rodzic
commit
1e0facb7ee

+ 5 - 30
Userland/Libraries/LibJS/Runtime/Value.cpp

@@ -1178,36 +1178,11 @@ ThrowCompletionOr<Value> mod(GlobalObject& global_object, Value lhs, Value rhs)
     auto rhs_numeric = TRY(rhs.to_numeric(global_object));
     auto rhs_numeric = TRY(rhs.to_numeric(global_object));
     if (both_number(lhs_numeric, rhs_numeric)) {
     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
         // 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);
+        // The ECMA specification is describing the mathematical definition of modulus
+        // implemented by fmod.
+        auto n = lhs_numeric.as_double();
+        auto d = rhs_numeric.as_double();
+        return Value(fmod(n, d));
     }
     }
     if (both_bigint(lhs_numeric, rhs_numeric)) {
     if (both_bigint(lhs_numeric, rhs_numeric)) {
         if (rhs_numeric.as_bigint().big_integer() == BIGINT_ZERO)
         if (rhs_numeric.as_bigint().big_integer() == BIGINT_ZERO)

+ 1 - 0
Userland/Libraries/LibJS/Tests/operators/modulo-basic.js

@@ -23,6 +23,7 @@ test("basic functionality", () => {
     expect(1 % -0).toBeNaN();
     expect(1 % -0).toBeNaN();
     expect(0 % 5).toBe(0);
     expect(0 % 5).toBe(0);
     expect(-0 % 5).toBe(-0);
     expect(-0 % 5).toBe(-0);
+    expect((-1) % -1).toBe(-0);
 
 
     // test262 examples
     // test262 examples
     expect(1 % null).toBeNaN();
     expect(1 % null).toBeNaN();

+ 3 - 0
Userland/Libraries/LibJS/Tests/test-common.js

@@ -53,6 +53,9 @@ class ExpectationError extends Error {
 
 
     const valueToString = value => {
     const valueToString = value => {
         try {
         try {
+            if (value === 0 && 1 / value < 0) {
+                return "-0";
+            }
             return String(value);
             return String(value);
         } catch {
         } catch {
             // e.g for objects without a prototype, the above throws.
             // e.g for objects without a prototype, the above throws.