1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 20:27:35 +00:00

LibJS: Fix that leftshift for BigInts did not round down

For negative number this previously rounded towards zero instead of the
intended always rounding down.
This commit is contained in:
davidot 2022-08-20 00:23:11 +02:00 committed by Linus Groh
parent cb49c07fb7
commit e663504df1
3 changed files with 37 additions and 4 deletions

View file

@ -1028,11 +1028,24 @@ ThrowCompletionOr<Value> left_shift(VM& vm, Value lhs, Value rhs)
return Value(lhs_i32 << rhs_u32);
}
if (both_bigint(lhs_numeric, rhs_numeric)) {
// 6.1.6.2.9 BigInt::leftShift ( x, y ), https://tc39.es/ecma262/#sec-numeric-types-bigint-leftShift
auto multiplier_divisor = Crypto::SignedBigInteger { Crypto::NumberTheory::Power(Crypto::UnsignedBigInteger(2), rhs_numeric.as_bigint().big_integer().unsigned_value()) };
if (rhs_numeric.as_bigint().big_integer().is_negative())
return Value(js_bigint(vm, lhs_numeric.as_bigint().big_integer().divided_by(multiplier_divisor).quotient));
else
return Value(js_bigint(vm, lhs_numeric.as_bigint().big_integer().multiplied_by(multiplier_divisor)));
// 1. If y < 0, then
if (rhs_numeric.as_bigint().big_integer().is_negative()) {
// a. Return the BigInt value that represents (x) / 2^-y, rounding down to the nearest integer, including for negative numbers.
// NOTE: Since y is negative we can just do (x) / 2^|y|
auto const& big_integer = lhs_numeric.as_bigint().big_integer();
auto division_result = big_integer.divided_by(multiplier_divisor);
// For positive initial values and no remainder just return quotient
if (division_result.remainder.is_zero() || !big_integer.is_negative())
return js_bigint(vm, division_result.quotient);
// For negative round "down" to the next negative number
return js_bigint(vm, division_result.quotient.minus(Crypto::SignedBigInteger { 1 }));
}
// 2. Return the BigInt value that represents (x) × 2^y.
return Value(js_bigint(vm, lhs_numeric.as_bigint().big_integer().multiplied_by(multiplier_divisor)));
}
return vm.throw_completion<TypeError>(ErrorType::BigIntBadOperatorOtherType, "left-shift");
}

View file

@ -63,3 +63,13 @@ test("shifting with non-numeric values", () => {
expect(Infinity << Infinity).toBe(0);
expect(-Infinity << Infinity).toBe(0);
});
describe("logical left shift on big ints", () => {
expect(3n << -1n).toBe(1n);
expect(3n << -2n).toBe(0n);
expect(-3n << -1n).toBe(-2n);
expect(-3n << -2n).toBe(-1n);
expect(-3n << -128n).toBe(-1n);
expect(3n << 6n).toBe(192n);
expect(3n << 0n).toBe(3n);
});

View file

@ -64,3 +64,13 @@ test("shifting with non-numeric values", () => {
expect(Infinity >> Infinity).toBe(0);
expect(-Infinity >> Infinity).toBe(0);
});
describe("logical right shift on big ints", () => {
expect(3n >> 1n).toBe(1n);
expect(3n >> 2n).toBe(0n);
expect(-3n >> 1n).toBe(-2n);
expect(-3n >> 2n).toBe(-1n);
expect(-3n >> 128n).toBe(-1n);
expect(-3n >> -6n).toBe(-192n);
expect(-3n >> 0n).toBe(-3n);
});