Commit 94b88943 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Implement comparisons with Numbers/Strings

This patch provides "Abstract Comparison" functions on the BigInt
class for comparing BigInts to Numbers and Strings.
The functionality is not exposed to JavaScript yet.

Bug: v8:6791
Change-Id: I835f290203a31f363970b1edb359e19af6dabc5d
Reviewed-on: https://chromium-review.googlesource.com/722324
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48718}
parent 8c98e07c
...@@ -113,7 +113,8 @@ MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string, ...@@ -113,7 +113,8 @@ MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string,
// 0x -> hex // 0x -> hex
// 0o -> octal // 0o -> octal
// 0b -> binary // 0b -> binary
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, const char* string); V8_EXPORT_PRIVATE MaybeHandle<BigInt> StringToBigInt(Isolate* isolate,
const char* string);
const int kDoubleToCStringMinBufferSize = 100; const int kDoubleToCStringMinBufferSize = 100;
......
...@@ -627,7 +627,7 @@ bool Object::StrictEquals(Object* that) { ...@@ -627,7 +627,7 @@ bool Object::StrictEquals(Object* that) {
return String::cast(this)->Equals(String::cast(that)); return String::cast(this)->Equals(String::cast(that));
} else if (this->IsBigInt()) { } else if (this->IsBigInt()) {
if (!that->IsBigInt()) return false; if (!that->IsBigInt()) return false;
return BigInt::Equal(BigInt::cast(this), BigInt::cast(that)); return BigInt::EqualToBigInt(BigInt::cast(this), BigInt::cast(that));
} }
return this == that; return this == that;
} }
...@@ -2437,7 +2437,7 @@ bool Object::SameValue(Object* other) { ...@@ -2437,7 +2437,7 @@ bool Object::SameValue(Object* other) {
return String::cast(this)->Equals(String::cast(other)); return String::cast(this)->Equals(String::cast(other));
} }
if (IsBigInt() && other->IsBigInt()) { if (IsBigInt() && other->IsBigInt()) {
return BigInt::Equal(BigInt::cast(this), BigInt::cast(other)); return BigInt::EqualToBigInt(BigInt::cast(this), BigInt::cast(other));
} }
return false; return false;
} }
...@@ -2457,7 +2457,7 @@ bool Object::SameValueZero(Object* other) { ...@@ -2457,7 +2457,7 @@ bool Object::SameValueZero(Object* other) {
return String::cast(this)->Equals(String::cast(other)); return String::cast(this)->Equals(String::cast(other));
} }
if (IsBigInt() && other->IsBigInt()) { if (IsBigInt() && other->IsBigInt()) {
return BigInt::Equal(BigInt::cast(this), BigInt::cast(other)); return BigInt::EqualToBigInt(BigInt::cast(this), BigInt::cast(other));
} }
return false; return false;
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "src/objects/bigint.h" #include "src/objects/bigint.h"
#include "src/double.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
namespace v8 { namespace v8 {
...@@ -165,15 +166,37 @@ MaybeHandle<BigInt> BigInt::UnsignedRightShift(Handle<BigInt> x, ...@@ -165,15 +166,37 @@ MaybeHandle<BigInt> BigInt::UnsignedRightShift(Handle<BigInt> x,
BigInt); BigInt);
} }
bool BigInt::LessThan(Handle<BigInt> x, Handle<BigInt> y) { namespace {
if (x->sign() == y->sign()) {
// Produces comparison result for {left_negative} == sign(x) != sign(y).
ComparisonResult UnequalSign(bool left_negative) {
return left_negative ? ComparisonResult::kLessThan
: ComparisonResult::kGreaterThan;
}
// Produces result for |x| > |y|, with {both_negative} == sign(x) == sign(y);
ComparisonResult AbsoluteGreater(bool both_negative) {
return both_negative ? ComparisonResult::kLessThan
: ComparisonResult::kGreaterThan;
}
// Produces result for |x| < |y|, with {both_negative} == sign(x) == sign(y).
ComparisonResult AbsoluteLess(bool both_negative) {
return both_negative ? ComparisonResult::kGreaterThan
: ComparisonResult::kLessThan;
}
} // namespace
ComparisonResult BigInt::CompareToBigInt(Handle<BigInt> x, Handle<BigInt> y) {
bool x_sign = x->sign();
if (x_sign != y->sign()) return UnequalSign(x_sign);
int result = AbsoluteCompare(x, y); int result = AbsoluteCompare(x, y);
return x->sign() ? (result > 0) : (result < 0); if (result > 0) return AbsoluteGreater(x_sign);
} if (result < 0) return AbsoluteLess(x_sign);
return x->sign(); return ComparisonResult::kEqual;
} }
bool BigInt::Equal(BigInt* x, BigInt* y) { bool BigInt::EqualToBigInt(BigInt* x, BigInt* y) {
if (x->sign() != y->sign()) return false; if (x->sign() != y->sign()) return false;
if (x->length() != y->length()) return false; if (x->length() != y->length()) return false;
for (int i = 0; i < x->length(); i++) { for (int i = 0; i < x->length(); i++) {
...@@ -279,6 +302,173 @@ MaybeHandle<BigInt> BigInt::Decrement(Handle<BigInt> x) { ...@@ -279,6 +302,173 @@ MaybeHandle<BigInt> BigInt::Decrement(Handle<BigInt> x) {
return result; return result;
} }
bool BigInt::EqualToString(Handle<BigInt> x, Handle<String> y) {
Isolate* isolate = x->GetIsolate();
// a. Let n be StringToBigInt(y).
MaybeHandle<BigInt> maybe_n = StringToBigInt(isolate, y, 10);
// b. If n is NaN, return false.
Handle<BigInt> n;
if (!maybe_n.ToHandle(&n)) {
DCHECK(isolate->has_pending_exception());
isolate->clear_pending_exception();
return false;
}
// c. Return the result of x == n.
return EqualToBigInt(*x, *n);
}
bool BigInt::EqualToNumber(Handle<BigInt> x, Handle<Object> y) {
DCHECK(y->IsNumber());
// a. If x or y are any of NaN, +∞, or -∞, return false.
// b. If the mathematical value of x is equal to the mathematical value of y,
// return true, otherwise return false.
if (y->IsSmi()) {
int value = Smi::ToInt(*y);
if (value == 0) return x->is_zero();
// Any multi-digit BigInt is bigger than a Smi.
STATIC_ASSERT(sizeof(digit_t) >= sizeof(value));
return (x->length() == 1) && (x->sign() == (value < 0)) &&
(x->digit(0) ==
static_cast<digit_t>(std::abs(static_cast<int64_t>(value))));
}
DCHECK(y->IsHeapNumber());
double value = Handle<HeapNumber>::cast(y)->value();
return CompareToDouble(x, value) == ComparisonResult::kEqual;
}
ComparisonResult BigInt::CompareToNumber(Handle<BigInt> x, Handle<Object> y) {
DCHECK(y->IsNumber());
if (y->IsSmi()) {
bool x_sign = x->sign();
int y_value = Smi::ToInt(*y);
bool y_sign = (y_value < 0);
if (x_sign != y_sign) return UnequalSign(x_sign);
if (x->is_zero()) {
DCHECK(!y_sign);
return y_value == 0 ? ComparisonResult::kEqual
: ComparisonResult::kLessThan;
}
// Any multi-digit BigInt is bigger than a Smi.
STATIC_ASSERT(sizeof(digit_t) >= sizeof(y_value));
if (x->length() > 1) return AbsoluteGreater(x_sign);
digit_t abs_value = std::abs(static_cast<int64_t>(y_value));
digit_t x_digit = x->digit(0);
if (x_digit > abs_value) return AbsoluteGreater(x_sign);
if (x_digit < abs_value) return AbsoluteLess(x_sign);
return ComparisonResult::kEqual;
}
DCHECK(y->IsHeapNumber());
double value = Handle<HeapNumber>::cast(y)->value();
return CompareToDouble(x, value);
}
ComparisonResult BigInt::CompareToDouble(Handle<BigInt> x, double y) {
if (std::isnan(y)) return ComparisonResult::kUndefined;
if (y == V8_INFINITY) return ComparisonResult::kLessThan;
if (y == -V8_INFINITY) return ComparisonResult::kGreaterThan;
bool x_sign = x->sign();
// Note that this is different from the double's sign bit for -0. That's
// intentional because -0 must be treated like 0.
bool y_sign = (y < 0);
if (x_sign != y_sign) return UnequalSign(x_sign);
if (y == 0) {
DCHECK(!x_sign);
return x->is_zero() ? ComparisonResult::kEqual
: ComparisonResult::kGreaterThan;
}
if (x->is_zero()) {
DCHECK(!y_sign);
return ComparisonResult::kLessThan;
}
uint64_t double_bits = bit_cast<uint64_t>(y);
int raw_exponent =
static_cast<int>(double_bits >> Double::kPhysicalSignificandSize) & 0x7FF;
uint64_t mantissa = double_bits & Double::kSignificandMask;
// Non-finite doubles are handled above.
DCHECK_NE(raw_exponent, 0x7FF);
int exponent = raw_exponent - 0x3FF;
if (exponent < 0) {
// The absolute value of the double is less than 1. Only 0n has an
// absolute value smaller than that, but we've already covered that case.
DCHECK(!x->is_zero());
return AbsoluteGreater(x_sign);
}
int x_length = x->length();
digit_t x_msd = x->digit(x_length - 1);
int msd_leading_zeros = base::bits::CountLeadingZeros(x_msd);
int x_bitlength = x_length * kDigitBits - msd_leading_zeros;
int y_bitlength = exponent + 1;
if (x_bitlength < y_bitlength) return AbsoluteLess(x_sign);
if (x_bitlength > y_bitlength) return AbsoluteGreater(x_sign);
// At this point, we know that signs and bit lengths (i.e. position of
// the most significant bit in exponent-free representation) are identical.
// {x} is not zero, {y} is finite and not denormal.
// Now we virtually convert the double to an integer by shifting its
// mantissa according to its exponent, so it will align with the BigInt {x},
// and then we compare them bit for bit until we find a difference or the
// least significant bit.
// <----- 52 ------> <-- virtual trailing zeroes -->
// y / mantissa: 1yyyyyyyyyyyyyyyyy 0000000000000000000000000000000
// x / digits: 0001xxxx xxxxxxxx xxxxxxxx ...
// <--> <------>
// msd_topbit kDigitBits
//
mantissa |= Double::kHiddenBit;
const int kMantissaTopBit = 52; // 0-indexed.
// 0-indexed position of {x}'s most significant bit within the {msd}.
int msd_topbit = kDigitBits - 1 - msd_leading_zeros;
DCHECK_EQ(msd_topbit, (x_bitlength - 1) % kDigitBits);
// Shifted chunk of {mantissa} for comparing with {digit}.
digit_t compare_mantissa;
// Number of unprocessed bits in {mantissa}. We'll keep them shifted to
// the left (i.e. most significant part) of the underlying uint64_t.
int remaining_mantissa_bits = 0;
// First, compare the most significant digit against the beginning of
// the mantissa.
if (msd_topbit < kMantissaTopBit) {
remaining_mantissa_bits = (kMantissaTopBit - msd_topbit);
compare_mantissa = mantissa >> remaining_mantissa_bits;
mantissa = mantissa << (64 - remaining_mantissa_bits);
} else {
DCHECK(msd_topbit >= kMantissaTopBit);
compare_mantissa = mantissa << (msd_topbit - kMantissaTopBit);
mantissa = 0;
}
if (x_msd > compare_mantissa) return AbsoluteGreater(x_sign);
if (x_msd < compare_mantissa) return AbsoluteLess(x_sign);
// Then, compare additional digits against any remaining mantissa bits.
for (int digit_index = x_length - 2; digit_index >= 0; digit_index--) {
if (remaining_mantissa_bits > 0) {
remaining_mantissa_bits -= kDigitBits;
if (sizeof(mantissa) != sizeof(x_msd)) {
compare_mantissa = mantissa >> (64 - kDigitBits);
// "& 63" to appease compilers. kDigitBits is 32 here anyway.
mantissa = mantissa << (kDigitBits & 63);
} else {
compare_mantissa = mantissa;
mantissa = 0;
}
} else {
compare_mantissa = 0;
}
digit_t digit = x->digit(digit_index);
if (digit > compare_mantissa) return AbsoluteGreater(x_sign);
if (digit < compare_mantissa) return AbsoluteLess(x_sign);
}
// Integer parts are equal; check whether {y} has a fractional part.
if (mantissa != 0) {
DCHECK(remaining_mantissa_bits > 0);
return AbsoluteLess(x_sign);
}
return ComparisonResult::kEqual;
}
MaybeHandle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) { MaybeHandle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
Isolate* isolate = bigint->GetIsolate(); Isolate* isolate = bigint->GetIsolate();
if (bigint->is_zero()) { if (bigint->is_zero()) {
......
...@@ -17,7 +17,7 @@ namespace internal { ...@@ -17,7 +17,7 @@ namespace internal {
// UNDER CONSTRUCTION! // UNDER CONSTRUCTION!
// Arbitrary precision integers in JavaScript. // Arbitrary precision integers in JavaScript.
class BigInt : public HeapObject { class V8_EXPORT_PRIVATE BigInt : public HeapObject {
public: public:
// Implementation of the Spec methods, see: // Implementation of the Spec methods, see:
// https://tc39.github.io/proposal-bigint/#sec-numeric-types // https://tc39.github.io/proposal-bigint/#sec-numeric-types
...@@ -36,22 +36,29 @@ class BigInt : public HeapObject { ...@@ -36,22 +36,29 @@ class BigInt : public HeapObject {
Handle<BigInt> y); Handle<BigInt> y);
static MaybeHandle<BigInt> UnsignedRightShift(Handle<BigInt> x, static MaybeHandle<BigInt> UnsignedRightShift(Handle<BigInt> x,
Handle<BigInt> y); Handle<BigInt> y);
static bool LessThan(Handle<BigInt> x, Handle<BigInt> y); // More convenient version of "bool LessThan(x, y)".
static bool Equal(BigInt* x, BigInt* y); static ComparisonResult CompareToBigInt(Handle<BigInt> x, Handle<BigInt> y);
static bool EqualToBigInt(BigInt* x, BigInt* y);
static Handle<BigInt> BitwiseAnd(Handle<BigInt> x, Handle<BigInt> y); static Handle<BigInt> BitwiseAnd(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> BitwiseXor(Handle<BigInt> x, Handle<BigInt> y); static Handle<BigInt> BitwiseXor(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> BitwiseOr(Handle<BigInt> x, Handle<BigInt> y); static Handle<BigInt> BitwiseOr(Handle<BigInt> x, Handle<BigInt> y);
// Other parts of the public interface.
static MaybeHandle<BigInt> Increment(Handle<BigInt> x); static MaybeHandle<BigInt> Increment(Handle<BigInt> x);
static MaybeHandle<BigInt> Decrement(Handle<BigInt> x); static MaybeHandle<BigInt> Decrement(Handle<BigInt> x);
// Other parts of the public interface.
bool ToBoolean() { return !is_zero(); } bool ToBoolean() { return !is_zero(); }
uint32_t Hash() { uint32_t Hash() {
// TODO(jkummerow): Improve this. At least use length and sign. // TODO(jkummerow): Improve this. At least use length and sign.
return is_zero() ? 0 : ComputeIntegerHash(static_cast<uint32_t>(digit(0))); return is_zero() ? 0 : ComputeIntegerHash(static_cast<uint32_t>(digit(0)));
} }
static bool EqualToString(Handle<BigInt> x, Handle<String> y);
static bool EqualToNumber(Handle<BigInt> x, Handle<Object> y);
static ComparisonResult CompareToNumber(Handle<BigInt> x, Handle<Object> y);
// Exposed for tests, do not call directly. Use CompareToNumber() instead.
static ComparisonResult CompareToDouble(Handle<BigInt> x, double y);
DECL_CAST(BigInt) DECL_CAST(BigInt)
DECL_VERIFIER(BigInt) DECL_VERIFIER(BigInt)
DECL_PRINTER(BigInt) DECL_PRINTER(BigInt)
......
...@@ -19,7 +19,7 @@ RUNTIME_FUNCTION(Runtime_BigIntEqual) { ...@@ -19,7 +19,7 @@ RUNTIME_FUNCTION(Runtime_BigIntEqual) {
CONVERT_ARG_HANDLE_CHECKED(Object, lhs, 0); CONVERT_ARG_HANDLE_CHECKED(Object, lhs, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, rhs, 1); CONVERT_ARG_HANDLE_CHECKED(Object, rhs, 1);
bool result = lhs->IsBigInt() && rhs->IsBigInt() && bool result = lhs->IsBigInt() && rhs->IsBigInt() &&
BigInt::Equal(BigInt::cast(*lhs), BigInt::cast(*rhs)); BigInt::EqualToBigInt(BigInt::cast(*lhs), BigInt::cast(*rhs));
return *isolate->factory()->ToBoolean(result); return *isolate->factory()->ToBoolean(result);
} }
......
...@@ -63,6 +63,7 @@ v8_source_set("unittests_sources") { ...@@ -63,6 +63,7 @@ v8_source_set("unittests_sources") {
"base/sys-info-unittest.cc", "base/sys-info-unittest.cc",
"base/template-utils-unittest.cc", "base/template-utils-unittest.cc",
"base/utils/random-number-generator-unittest.cc", "base/utils/random-number-generator-unittest.cc",
"bigint-unittest.cc",
"cancelable-tasks-unittest.cc", "cancelable-tasks-unittest.cc",
"char-predicates-unittest.cc", "char-predicates-unittest.cc",
"code-stub-assembler-unittest.cc", "code-stub-assembler-unittest.cc",
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cmath>
#include "src/conversions.h"
#include "src/factory.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/objects/bigint-inl.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
typedef TestWithIsolate BigIntWithIsolate;
void Compare(Handle<BigInt> x, double value, ComparisonResult expected) {
CHECK_EQ(expected, BigInt::CompareToDouble(x, value));
}
TEST_F(BigIntWithIsolate, CompareToDouble) {
Factory* factory = isolate()->factory();
Handle<BigInt> zero = factory->NewBigIntFromInt(0);
Handle<BigInt> one = factory->NewBigIntFromInt(1);
Handle<BigInt> minus_one = factory->NewBigIntFromInt(-1);
// Non-finite doubles.
Compare(zero, std::nan(""), ComparisonResult::kUndefined);
Compare(one, INFINITY, ComparisonResult::kLessThan);
Compare(one, -INFINITY, ComparisonResult::kGreaterThan);
// Unequal sign.
Compare(one, -1, ComparisonResult::kGreaterThan);
Compare(minus_one, 1, ComparisonResult::kLessThan);
// Cases involving zero.
Compare(zero, 0, ComparisonResult::kEqual);
Compare(zero, -0, ComparisonResult::kEqual);
Compare(one, 0, ComparisonResult::kGreaterThan);
Compare(minus_one, 0, ComparisonResult::kLessThan);
Compare(zero, 1, ComparisonResult::kLessThan);
Compare(zero, -1, ComparisonResult::kGreaterThan);
// Small doubles.
Compare(zero, 0.25, ComparisonResult::kLessThan);
Compare(one, 0.5, ComparisonResult::kGreaterThan);
Compare(one, -0.5, ComparisonResult::kGreaterThan);
Compare(zero, -0.25, ComparisonResult::kGreaterThan);
Compare(minus_one, -0.5, ComparisonResult::kLessThan);
// Different bit lengths.
Handle<BigInt> four = factory->NewBigIntFromInt(4);
Handle<BigInt> minus_five = factory->NewBigIntFromInt(-5);
Compare(four, 3.9, ComparisonResult::kGreaterThan);
Compare(four, 1.5, ComparisonResult::kGreaterThan);
Compare(four, 8, ComparisonResult::kLessThan);
Compare(four, 16, ComparisonResult::kLessThan);
Compare(minus_five, -4.9, ComparisonResult::kLessThan);
Compare(minus_five, -4, ComparisonResult::kLessThan);
Compare(minus_five, -25, ComparisonResult::kGreaterThan);
// Same bit length, difference in first digit.
double big_double = 4428155326412785451008.0;
Handle<BigInt> big =
StringToBigInt(isolate(), "0xF10D00000000000000").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kGreaterThan);
big = StringToBigInt(isolate(), "0xE00D00000000000000").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kLessThan);
double other_double = -13758438578910658560.0;
Handle<BigInt> other =
StringToBigInt(isolate(), "-0xBEEFC1FE00000000").ToHandleChecked();
Compare(other, other_double, ComparisonResult::kGreaterThan);
other = StringToBigInt(isolate(), "-0xBEEFCBFE00000000").ToHandleChecked();
Compare(other, other_double, ComparisonResult::kLessThan);
// Same bit length, difference in non-first digit.
big = StringToBigInt(isolate(), "0xF00D00000000000001").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kGreaterThan);
big = StringToBigInt(isolate(), "0xF00A00000000000000").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kLessThan);
other = StringToBigInt(isolate(), "-0xBEEFCAFE00000001").ToHandleChecked();
Compare(other, other_double, ComparisonResult::kLessThan);
// Same bit length, difference in fractional part.
Compare(one, 1.5, ComparisonResult::kLessThan);
Compare(minus_one, -1.25, ComparisonResult::kGreaterThan);
big = factory->NewBigIntFromInt(0xF00D00);
Compare(big, 15731968.125, ComparisonResult::kLessThan);
Compare(big, 15731967.875, ComparisonResult::kGreaterThan);
big = StringToBigInt(isolate(), "0x123456789ab").ToHandleChecked();
Compare(big, 1250999896491.125, ComparisonResult::kLessThan);
// Equality!
Compare(one, 1, ComparisonResult::kEqual);
Compare(minus_one, -1, ComparisonResult::kEqual);
big = StringToBigInt(isolate(), "0xF00D00000000000000").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kEqual);
Handle<BigInt> two_52 =
StringToBigInt(isolate(), "0x10000000000000").ToHandleChecked();
Compare(two_52, 4503599627370496.0, ComparisonResult::kEqual);
}
} // namespace internal
} // namespace v8
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
'base/sys-info-unittest.cc', 'base/sys-info-unittest.cc',
'base/template-utils-unittest.cc', 'base/template-utils-unittest.cc',
'base/utils/random-number-generator-unittest.cc', 'base/utils/random-number-generator-unittest.cc',
'bigint-unittest.cc',
'cancelable-tasks-unittest.cc', 'cancelable-tasks-unittest.cc',
'char-predicates-unittest.cc', 'char-predicates-unittest.cc',
"code-stub-assembler-unittest.cc", "code-stub-assembler-unittest.cc",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment