mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 21:07:35 +00:00
LibCrypto+LibJS: Better bigint bitwise_and binop
Bitwise and is defined in terms of two's complement, so some converting needs to happen for SignedBigInteger's sign/magnitude representation to work out. UnsignedBigInteger::bitwise_not() is repurposed to convert all high-order zero bits to ones up to a limit, for the two's complement conversion to work. Fixes test262/test/language/expressions/bitwise-and/bigint.js.
This commit is contained in:
parent
945d962322
commit
1f98639396
7 changed files with 52 additions and 23 deletions
|
@ -194,12 +194,36 @@ FLATTEN SignedBigInteger SignedBigInteger::bitwise_or(const SignedBigInteger& ot
|
|||
|
||||
FLATTEN SignedBigInteger SignedBigInteger::bitwise_and(const SignedBigInteger& other) const
|
||||
{
|
||||
auto result = bitwise_and(other.unsigned_value());
|
||||
if (!is_negative() && !other.is_negative())
|
||||
return { unsigned_value().bitwise_and(other.unsigned_value()), false };
|
||||
|
||||
// The sign bit will have to be AND'd manually.
|
||||
result.m_sign = is_negative() && other.is_negative();
|
||||
// These two just use that -x == ~x + 1 (see below).
|
||||
|
||||
return result;
|
||||
// -A & B == (~A + 1) & B.
|
||||
if (is_negative() && !other.is_negative())
|
||||
return { unsigned_value().bitwise_not_fill_to_size(other.trimmed_length()).plus(1).bitwise_and(other.unsigned_value()), false };
|
||||
|
||||
// A & -B == A & (~B + 1).
|
||||
if (!is_negative() && other.is_negative())
|
||||
return { unsigned_value().bitwise_and(other.unsigned_value().bitwise_not_fill_to_size(trimmed_length()).plus(1)), false };
|
||||
|
||||
// Both numbers are negative.
|
||||
// x + ~x == 0xff...ff, up to however many bits x is wide.
|
||||
// In two's complement, x + ~x + 1 == 0 since the 1 in the overflowing bit position is masked out.
|
||||
// Rearranging terms, ~x = -x - 1 (eq1).
|
||||
// Substituting x = y - 1, ~(y - 1) == -(y - 1) - 1 == -y +1 -1 == -y, or ~(y - 1) == -y (eq2).
|
||||
// Since both numbers are negative, we want to compute -A & -B.
|
||||
// Per (eq2):
|
||||
// -A & -B == ~(A - 1) & ~(B - 1)
|
||||
// Inverting both sides:
|
||||
// ~(-A & -B) == ~(~(A - 1) & ~(B - 1)) == ~~(A - 1) | ~~(B - 1) == (A - 1) | (B - 1).
|
||||
// Applying (q1) on the LHS:
|
||||
// -(-A & -B) - 1 == (A - 1) | (B - 1)
|
||||
// Adding 1 on both sides and then multiplying both sides by -1:
|
||||
// -A & -B == -( (A - 1) | (B - 1) + 1)
|
||||
// So we can compute the bitwise and by returning a negative number with magnitude (A - 1) | (B - 1) + 1.
|
||||
// This is better than the naive (~A + 1) & (~B + 1) because it needs just one O(n) scan for the or instead of 2 for the ~s.
|
||||
return { unsigned_value().minus(1).bitwise_or(other.unsigned_value().minus(1)).plus(1), true };
|
||||
}
|
||||
|
||||
FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(const SignedBigInteger& other) const
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue