runtime/int-builtins.cpp (874 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "int-builtins.h"
#include <cinttypes>
#include <climits>
#include <cmath>
#include "builtins.h"
#include "bytes-builtins.h"
#include "formatter.h"
#include "frame.h"
#include "globals.h"
#include "objects.h"
#include "str-builtins.h"
#include "thread.h"
#include "type-builtins.h"
namespace py {
bool intDivmodNear(Thread* thread, const Int& dividend, const Int& divisor,
Object* quotient, Object* remainder) {
Runtime* runtime = thread->runtime();
if (!runtime->intDivideModulo(thread, dividend, divisor, quotient,
remainder)) {
return false;
}
HandleScope scope(thread);
Int quotient_int(&scope, **quotient);
Int remainder_int(&scope, **remainder);
Int one(&scope, SmallInt::fromWord(1));
Int twice_remainder(&scope,
runtime->intBinaryLshift(thread, remainder_int, one));
word cmp = twice_remainder.compare(*divisor);
if ((divisor.isNegative() ? cmp < 0 : cmp > 0) ||
(cmp == 0 && quotient_int.isOdd())) {
*quotient = runtime->intAdd(thread, quotient_int, one);
*remainder = runtime->intSubtract(thread, remainder_int, divisor);
}
return true;
}
word largeIntHash(RawLargeInt value) {
const word bits_per_half = kBitsPerWord / 2;
// The following computes `value % modulus` with
// `modulus := kArithmeticHashModulus` with
// a C/C++ style modulo (so -17 % m == -17). This matches cpythons hash
// function (see `cpython/Objects/longobject.c` for details).
// The following describes how we can compute without actually performing
// any division/modulo operations just by bit-shifting. We want to compute
// the modulo by a prime number for good hashing behavior and we pick a
// Mersenne prime, because that allows us to perform some masking tricks
// below. We use the constants as follows:
// hash_bits := kArithmeticHashBits := 61
// modulus := kArithmeticHashModulus := (1 << hash_bits) - 1
//
// To compute the modulo of a large int, we split it into higher bits and
// lower bits:
// large_int % modulus = ((high << s) + remaining_bits) % modulus
// = (((high << s) % modulus) + remaining_bits % modulus) % modulus
//
// It turns out the upper part part of this equation
// `((high << s) % modulus)` is just a bit rotation of `high_bits`.
// To understand this consider splitting up a value into `high_bits` and
// `low_bits`:
// low_bits := val & modulus
// high_bits := ((val >> hash_bits) << hash_bits)
// val = high_bits + low_bits
//
// <=> val << s = (((val << s) >> hash_bits) << hash_bits)
// + (val << s) & modulus
// = ((val >> (hash_bits - s)) << hash_bits)
// + (val << s) & modulus
// <=> (val << s) % modulus
// = (((val >> (hash_bits - s)) << hash_bits) % modulus +
// ((val << s) & modulus) % modulus) % modulus
// = (((val >> (hash_bits - s)) * 2**hash_bits) % modulus +
// ((val << s) & modulus) % modulus
// = (((val << (hash_bits - s)) % modulus * 1) % modulus +
// ((val << s) & modulus) % modulus
// = (((val << (hash_bits - s)) % modulus +
// ((val << s) & modulus) % modulus
// = ((val << (hash_bits - s)) + ((val << s) & modulus))
// % modulus
// Which is a rotation of `s` bits in the lowest `hash_bits` in `val`.
//
// Note that we can choose any size `s` that is smaller than `hash_bits`,
// meaning we can design our algorithm to compute `s` bits at a time.
//
// We only add a small amount of bits in each step, so the remaining modulo
// operation can be expressed as `if (result >= modulus) result -= modulus;`.
bool is_negative = value.isNegative();
word num_digits = value.numDigits();
uword result = 0;
for (word i = num_digits - 1; i >= 0; i--) {
uword digit = value.digitAt(i);
// The computation is designed for positive numbers. We compute negative
// numbers via `-(-value % p)`. We use the following equivalence so we do
// not need to negate the large integer:
// -(-value % p)
// <=> -((~value + 1) % p)
// <=> -(((~value % p) + (1 % p)) % p)
// <=> -(((~value % p) + 1) % p)
if (is_negative) {
digit = ~digit;
}
// Rotate result, add upper half of the digit, perform modulo.
result = ((result << bits_per_half) & kArithmeticHashModulus) |
result >> (kArithmeticHashBits - bits_per_half);
result += digit >> bits_per_half;
if (result >= kArithmeticHashModulus) {
result -= kArithmeticHashModulus;
}
// Rotate result, add lower half of digit, perform modulo.
result = ((result << bits_per_half) & kArithmeticHashModulus) |
result >> (kArithmeticHashBits - bits_per_half);
uword low_bits = digit & ((uword{1} << bits_per_half) - 1);
result += low_bits;
if (result >= kArithmeticHashModulus) {
result -= kArithmeticHashModulus;
}
}
if (is_negative) {
// We computed `result := ~value % p` so far, as described above compute
// `-((result + 1) % p)` now.
result++;
if (result >= kArithmeticHashModulus) {
result -= kArithmeticHashModulus;
}
result = -result;
// cpython replaces `-1` results with -2, because -1 is used as an
// "uninitialized hash" marker in some situations. We do not use the same
// marker, but do the same to match behavior.
if (result == static_cast<uword>(word{-1})) {
result--;
}
} else {
DCHECK(result != static_cast<uword>(word{-1}),
"should only have -1 for negative numbers");
}
return result;
}
// Used only for UserIntBase as a heap-allocated object.
static const BuiltinAttribute kUserIntBaseAttributes[] = {
{ID(_UserInt__value), RawUserIntBase::kValueOffset,
AttributeFlags::kHidden},
};
void initializeIntTypes(Thread* thread) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Type int_type(&scope, addBuiltinType(thread, ID(int), LayoutId::kInt,
/*superclass_id=*/LayoutId::kObject,
kUserIntBaseAttributes,
UserIntBase::kSize, /*basetype=*/true));
{
Type type(&scope,
addImmediateBuiltinType(thread, ID(largeint), LayoutId::kLargeInt,
/*builtin_base=*/LayoutId::kInt,
/*superclass_id=*/LayoutId::kObject,
/*basetype=*/false));
Layout layout(&scope, type.instanceLayout());
layout.setDescribedType(*int_type);
runtime->setLargeIntType(type);
}
{
Type type(&scope,
addImmediateBuiltinType(thread, ID(smallint), LayoutId::kSmallInt,
/*builtin_base=*/LayoutId::kInt,
/*superclass_id=*/LayoutId::kObject,
/*basetype=*/false));
Layout layout(&scope, type.instanceLayout());
layout.setDescribedType(*int_type);
runtime->setSmallIntType(type);
// We want to lookup the class of an immediate type by using the 5-bit tag
// value as an index into the class table. Replicate the layout object for
// SmallInt to all locations that decode to a SmallInt tag.
for (word i = 2; i < (1 << Object::kImmediateTagBits); i += 2) {
DCHECK(
runtime->layoutAt(static_cast<LayoutId>(i)) == SmallInt::fromWord(0),
"list collision");
runtime->layoutAtPut(static_cast<LayoutId>(i), *layout);
}
}
addImmediateBuiltinType(thread, ID(bool), LayoutId::kBool,
/*builtin_base=*/LayoutId::kInt,
/*superclass_id=*/LayoutId::kInt,
/*basetype=*/false);
}
RawObject convertBoolToInt(RawObject object) {
DCHECK(object.isBool(), "conversion from bool to int requires a bool object");
return RawSmallInt::fromWord(object == RawBool::trueObj() ? 1 : 0);
}
static RawObject intBinaryOpSubclass(Thread* thread, Arguments args,
RawObject (*op)(Thread* t, const Int& left,
const Int& right)) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Object other_obj(&scope, args.get(1));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfInt(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(int));
}
if (!runtime->isInstanceOfInt(*other_obj)) {
return NotImplementedType::object();
}
Int self(&scope, intUnderlying(*self_obj));
Int other(&scope, intUnderlying(*other_obj));
return op(thread, self, other);
}
inline static RawObject intBinaryOp(Thread* thread, Arguments args,
RawObject (*op)(Thread* t, const Int& left,
const Int& right)) {
if (args.get(0).isInt() && args.get(1).isInt()) {
HandleScope scope(thread);
Int self(&scope, args.get(0));
Int other(&scope, args.get(1));
return op(thread, self, other);
}
return intBinaryOpSubclass(thread, args, op);
}
static word gcd(word dividend, word divisor) {
while (divisor > 0) {
word modulo = dividend % divisor;
dividend = divisor;
divisor = modulo;
}
return dividend;
}
RawObject intGCD(Thread* thread, const Int& a, const Int& b) {
Runtime* runtime = thread->runtime();
if (a.isSmallInt() && b.isSmallInt()) {
// Take a shortcut because we know the result fits in a word.
word small_dividend = SmallInt::cast(*a).value();
word small_divisor = SmallInt::cast(*b).value();
if (small_dividend < 0) {
small_dividend = -small_dividend;
}
if (small_divisor < 0) {
small_divisor = -small_divisor;
}
if (small_dividend < small_divisor) {
word temp = small_divisor;
small_divisor = small_dividend;
small_dividend = temp;
}
return runtime->newInt(gcd(small_dividend, small_divisor));
}
HandleScope scope(thread);
Int dividend(&scope, *a);
Int divisor(&scope, *b);
if (dividend.isNegative()) {
dividend = runtime->intNegate(thread, dividend);
}
if (divisor.isNegative()) {
divisor = runtime->intNegate(thread, divisor);
}
if (dividend.compare(*divisor) < 0) {
Object temp(&scope, *divisor);
divisor = *dividend;
dividend = *temp;
}
while (divisor.isPositive()) {
if (dividend.isSmallInt() || dividend.isBool()) {
// Take a shortcut because we know the result fits in a word.
word small_dividend = dividend.asWord();
word small_divisor = divisor.asWord();
return runtime->newInt(gcd(small_dividend, small_divisor));
}
Object modulo(&scope, NoneType::object());
bool division_succeeded =
runtime->intDivideModulo(thread, dividend, divisor, nullptr, &modulo);
DCHECK(division_succeeded, "divisor_int must be nonzero");
dividend = *divisor;
divisor = *modulo;
}
return *dividend;
}
static RawObject intUnaryOp(Thread* thread, Arguments args,
RawObject (*op)(Thread* t, const Int& self)) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfInt(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(int));
}
Int self(&scope, intUnderlying(*self_obj));
return op(thread, self);
}
static RawObject asInt(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread*, const Int& self) -> RawObject {
if (self.isBool()) {
return convertBoolToInt(*self);
}
return *self;
});
}
static RawObject asStr(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread* t, const Int& self) {
return formatIntDecimalSimple(t, self);
});
}
RawObject METH(int, __abs__)(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread* t, const Int& self) -> RawObject {
if (self.isNegative()) {
return t->runtime()->intNegate(t, self);
}
if (self.isBool()) return convertBoolToInt(*self);
return *self;
});
}
RawObject METH(int, __add__)(Thread* thread, Arguments args) {
return intBinaryOp(thread, args,
[](Thread* t, const Int& left, const Int& right) {
return t->runtime()->intAdd(t, left, right);
});
}
RawObject METH(int, __and__)(Thread* thread, Arguments args) {
return intBinaryOp(thread, args,
[](Thread* t, const Int& left, const Int& right) {
return t->runtime()->intBinaryAnd(t, left, right);
});
}
RawObject METH(int, __bool__)(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread*, const Int& self) -> RawObject {
if (self.isBool()) return *self;
if (self.isSmallInt()) {
return Bool::fromBool(SmallInt::cast(*self).value() != 0);
}
DCHECK(self.isLargeInt(), "remaining case should be LargeInt");
return Bool::trueObj();
});
}
RawObject METH(int, __ceil__)(Thread* thread, Arguments args) {
return asInt(thread, args);
}
RawObject METH(int, __eq__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args,
[](Thread*, const Int& left, const Int& right) -> RawObject {
return Bool::fromBool(left.compare(*right) == 0);
});
}
RawObject METH(int, __divmod__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args,
[](Thread* t, const Int& left, const Int& right) -> RawObject {
HandleScope scope(t);
Object quotient(&scope, NoneType::object());
Object remainder(&scope, NoneType::object());
Runtime* runtime = t->runtime();
if (!runtime->intDivideModulo(t, left, right, "ient, &remainder)) {
return t->raiseWithFmt(LayoutId::kZeroDivisionError,
"integer division or modulo by zero");
}
return runtime->newTupleWith2(quotient, remainder);
});
}
RawObject METH(int, __float__)(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread* t, const Int& self) {
HandleScope scope(t);
double value = 0.0;
Object maybe_error(&scope, convertIntToDouble(t, self, &value));
if (!maybe_error.isNoneType()) return *maybe_error;
return t->runtime()->newFloat(value);
});
}
RawObject METH(int, __floor__)(Thread* thread, Arguments args) {
return asInt(thread, args);
}
RawObject METH(int, __invert__)(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread* t, const Int& self) {
return t->runtime()->intInvert(t, self);
});
}
RawObject METH(int, __floordiv__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args, [](Thread* t, const Int& left, const Int& right) {
HandleScope scope(t);
Object quotient(&scope, NoneType::object());
if (!t->runtime()->intDivideModulo(t, left, right, "ient,
nullptr)) {
return t->raiseWithFmt(LayoutId::kZeroDivisionError,
"integer division or modulo by zero");
}
return *quotient;
});
}
static RawObject formatIntCodePoint(Thread* thread, const Int& value,
FormatSpec* format) {
if (value.isLargeInt()) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"Python int too large to convert to C long");
}
word value_word = value.asWord();
if (value_word < 0 || value_word > kMaxUnicode) {
static_assert(kMaxUnicode == 0x10ffff, "unexpected max unicode value");
return thread->raiseWithFmt(LayoutId::kOverflowError,
"%%c arg not in range(0x110000)");
}
HandleScope scope(thread);
Str code_point(&scope,
SmallStr::fromCodePoint(static_cast<int32_t>(value_word)));
if (format->precision >= 0) {
return thread->raiseWithFmt(
LayoutId::kValueError,
"Precision not allowed in integer format specifier");
}
if (format->positive_sign != '\0') {
return thread->raiseWithFmt(
LayoutId::kValueError,
"Sign not allowed with integer format specifier 'c'");
}
if (format->alternate) {
return thread->raiseWithFmt(
LayoutId::kValueError,
"Alternate form (#) not allowed with integer format specifier 'c'");
}
return formatStr(thread, code_point, format);
}
RawObject METH(int, __format__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfInt(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(int));
}
Int self(&scope, intUnderlying(*self_obj));
Object spec_obj(&scope, args.get(1));
if (!runtime->isInstanceOfStr(*spec_obj)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"__format__() argument 1 must be str, not %T",
&spec_obj);
}
Str spec(&scope, strUnderlying(*spec_obj));
if (spec.length() == 0) {
// We return the equivalent of `str(self)` for an empty spec.
if (self_obj.isSmallInt() || self_obj.isLargeInt()) {
return formatIntDecimalSimple(thread, self);
}
if (self_obj.isBool()) {
return runtime->symbols()->at(Bool::cast(*self_obj).value() ? ID(True)
: ID(False));
}
Object value(&scope, thread->invokeMethod1(self_obj, ID(__str__)));
DCHECK(!value.isErrorNotFound(), "`__str__` should always exist");
if (value.isErrorException()) return *value;
if (!runtime->isInstanceOfStr(*value)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"__str__ returned non-string (type %T)",
&value);
}
return *value;
}
FormatSpec format;
Object possible_error(&scope,
parseFormatSpec(thread, spec,
/*default_type=*/'d',
/*default_align=*/'>', &format));
if (!possible_error.isNoneType()) {
DCHECK(possible_error.isErrorException(), "expected exception");
return *possible_error;
}
switch (format.type) {
case 'b':
return formatIntBinary(thread, self, &format);
case 'c':
return formatIntCodePoint(thread, self, &format);
case 'd':
return formatIntDecimal(thread, self, &format);
case 'n':
UNIMPLEMENTED("print with locale thousands separator");
case 'o':
return formatIntOctal(thread, self, &format);
case 'x':
return formatIntHexadecimalLowerCase(thread, self, &format);
case 'X':
return formatIntHexadecimalUpperCase(thread, self, &format);
case '%':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G': {
double value;
Object err(&scope, convertIntToDouble(thread, self, &value));
if (err.isErrorException()) return *err;
return formatFloat(thread, value, &format);
}
default:
return raiseUnknownFormatError(thread, format.type, self_obj);
}
}
RawObject METH(int, __hash__)(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread*, const Int& self) -> RawObject {
return SmallInt::fromWord(intHash(*self));
});
}
RawObject METH(int, __index__)(Thread* thread, Arguments args) {
return asInt(thread, args);
}
RawObject METH(int, __int__)(Thread* thread, Arguments args) {
return asInt(thread, args);
}
RawObject METH(int, __le__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args,
[](Thread*, const Int& left, const Int& right) -> RawObject {
return Bool::fromBool(left.compare(*right) <= 0);
});
}
RawObject METH(int, __lt__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args,
[](Thread*, const Int& left, const Int& right) -> RawObject {
return Bool::fromBool(left.compare(*right) < 0);
});
}
RawObject METH(int, __ge__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args,
[](Thread*, const Int& left, const Int& right) -> RawObject {
return Bool::fromBool(left.compare(*right) >= 0);
});
}
RawObject METH(int, __gt__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args,
[](Thread*, const Int& left, const Int& right) -> RawObject {
return Bool::fromBool(left.compare(*right) > 0);
});
}
RawObject METH(int, __mod__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args, [](Thread* t, const Int& left, const Int& right) {
HandleScope scope(t);
Object remainder(&scope, NoneType::object());
if (!t->runtime()->intDivideModulo(t, left, right, nullptr,
&remainder)) {
return t->raiseWithFmt(LayoutId::kZeroDivisionError,
"integer division or modulo by zero");
}
return *remainder;
});
}
RawObject METH(int, __mul__)(Thread* thread, Arguments args) {
return intBinaryOp(thread, args,
[](Thread* t, const Int& left, const Int& right) {
return t->runtime()->intMultiply(t, left, right);
});
}
RawObject METH(int, __ne__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args,
[](Thread*, const Int& left, const Int& right) -> RawObject {
return Bool::fromBool(left.compare(*right) != 0);
});
}
RawObject METH(int, __neg__)(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread* t, const Int& self) {
return t->runtime()->intNegate(t, self);
});
}
RawObject METH(int, __rshift__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args, [](Thread* t, const Int& left, const Int& right) {
if (right.isNegative()) {
return t->raiseWithFmt(LayoutId::kValueError, "negative shift count");
}
return t->runtime()->intBinaryRshift(t, left, right);
});
}
RawObject METH(int, __str__)(Thread* thread, Arguments args) {
return asStr(thread, args);
}
RawObject METH(int, __sub__)(Thread* thread, Arguments args) {
return intBinaryOp(thread, args,
[](Thread* t, const Int& left, const Int& right) {
return t->runtime()->intSubtract(t, left, right);
});
}
RawObject METH(int, __truediv__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args, [](Thread* t, const Int& left, const Int& right) {
if (right.isZero()) {
return t->raiseWithFmt(LayoutId::kZeroDivisionError,
"division by zero");
}
if (left.isLargeInt() || right.isLargeInt()) {
UNIMPLEMENTED("true division of LargeInts"); // TODO(T40072578)
}
return t->runtime()->newFloat(static_cast<double>(left.asWord()) /
right.asWord());
});
}
RawObject METH(int, __trunc__)(Thread* thread, Arguments args) {
return asInt(thread, args);
}
RawObject METH(int, __xor__)(Thread* thread, Arguments args) {
return intBinaryOp(thread, args,
[](Thread* t, const Int& left, const Int& right) {
return t->runtime()->intBinaryXor(t, left, right);
});
}
RawObject METH(int, __or__)(Thread* thread, Arguments args) {
return intBinaryOp(thread, args,
[](Thread* t, const Int& left, const Int& right) {
return t->runtime()->intBinaryOr(t, left, right);
});
}
RawObject METH(int, __lshift__)(Thread* thread, Arguments args) {
return intBinaryOp(
thread, args, [](Thread* t, const Int& left, const Int& right) {
if (right.isNegative()) {
return t->raiseWithFmt(LayoutId::kValueError, "negative shift count");
}
return t->runtime()->intBinaryLshift(t, left, right);
});
}
RawObject METH(int, __pos__)(Thread* thread, Arguments args) {
return asInt(thread, args);
}
RawObject METH(int, __repr__)(Thread* thread, Arguments args) {
return asStr(thread, args);
}
RawObject METH(int, __round__)(Thread* thread, Arguments args) {
return asInt(thread, args);
}
RawObject METH(int, as_integer_ratio)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfInt(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(int));
}
Object numerator(&scope, intUnderlying(*self_obj));
Object denominator(&scope, SmallInt::fromWord(1));
return runtime->newTupleWith2(numerator, denominator);
}
RawObject METH(int, bit_length)(Thread* thread, Arguments args) {
return intUnaryOp(thread, args, [](Thread* t, const Int& self) {
return t->runtime()->newInt(self.bitLength());
});
}
RawObject METH(int, conjugate)(Thread* thread, Arguments args) {
return asInt(thread, args);
}
static RawObject toBytesImpl(Thread* thread, const Object& self_obj,
const Object& length_obj,
const Object& byteorder_obj, bool is_signed) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfInt(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(int));
}
Int self(&scope, intUnderlying(*self_obj));
if (!runtime->isInstanceOfInt(*length_obj)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"length argument cannot be interpreted as an integer");
}
Int length_int(&scope, intUnderlying(*length_obj));
OptInt<word> l = length_int.asInt<word>();
if (l.error != CastError::None) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"Python int too large to convert to C word");
}
word length = l.value;
if (length < 0) {
return thread->raiseWithFmt(LayoutId::kValueError,
"length argument must be non-negative");
}
if (!runtime->isInstanceOfStr(*byteorder_obj)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"to_bytes() argument 2 must be str, not int");
}
Str byteorder(&scope, *byteorder_obj);
endian endianness;
if (byteorder.equals(runtime->symbols()->at(ID(little)))) {
endianness = endian::little;
} else if (byteorder.equals(runtime->symbols()->at(ID(big)))) {
endianness = endian::big;
} else {
return thread->raiseWithFmt(LayoutId::kValueError,
"byteorder must be either 'little' or 'big'");
}
if (!is_signed && self.isNegative()) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"can't convert negative int to unsigned");
}
// Check for overflow.
word num_digits = self.numDigits();
uword high_digit = self.digitAt(num_digits - 1);
word bit_length =
num_digits * kBitsPerWord - Utils::numRedundantSignBits(high_digit);
if (bit_length > length * kBitsPerByte + !is_signed) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"int too big to convert");
}
return runtime->intToBytes(thread, self, length, endianness);
}
RawObject METH(int, to_bytes)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
Object length(&scope, args.get(1));
Object byteorder(&scope, args.get(2));
if (!args.get(3).isBool()) {
return thread->raiseWithFmt(LayoutId::kTypeError, "signed must be bool");
}
return toBytesImpl(thread, self, length, byteorder,
Bool::cast(args.get(3)).value());
}
RawObject METH(bool, __new__)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object type_obj(&scope, args.get(0));
if (!runtime->isInstanceOfType(*type_obj)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"bool.__new__(X): X is not a type object");
}
Type type(&scope, *type_obj);
// Since bool can't be subclassed, only need to check if the type is exactly
// bool.
if (type.instanceLayoutId() != LayoutId::kBool) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"bool.__new__(X): X is not bool");
}
return Interpreter::isTrue(thread, args.get(1));
}
static RawObject boolOrImpl(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isBool()) {
return thread->raiseRequiresType(self_obj, ID(bool));
}
Bool self(&scope, *self_obj);
Object other_obj(&scope, args.get(1));
if (other_obj.isBool()) {
return Bool::fromBool(self.value() || Bool::cast(*other_obj).value());
}
if (runtime->isInstanceOfInt(*other_obj)) {
return intBinaryOp(thread, args,
[](Thread* t, const Int& left, const Int& right) {
return t->runtime()->intBinaryOr(t, left, right);
});
}
return NotImplementedType::object();
}
static RawObject boolXorImpl(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isBool()) {
return thread->raiseRequiresType(self_obj, ID(bool));
}
Bool self(&scope, *self_obj);
Object other_obj(&scope, args.get(1));
if (other_obj.isBool()) {
return Bool::fromBool(self.value() ^ Bool::cast(*other_obj).value());
}
if (runtime->isInstanceOfInt(*other_obj)) {
return intBinaryOp(thread, args,
[](Thread* t, const Int& left, const Int& right) {
return t->runtime()->intBinaryXor(t, left, right);
});
}
return NotImplementedType::object();
}
RawObject METH(bool, __or__)(Thread* thread, Arguments args) {
return boolOrImpl(thread, args);
}
RawObject METH(bool, __ror__)(Thread* thread, Arguments args) {
return boolOrImpl(thread, args);
}
RawObject METH(bool, __xor__)(Thread* thread, Arguments args) {
return boolXorImpl(thread, args);
}
RawObject METH(bool, __rxor__)(Thread* thread, Arguments args) {
return boolXorImpl(thread, args);
}
enum RoundingDirection {
RoundDown = -1,
NoRounding = 0,
RoundUp = 1,
};
// Convert a large int to double. Returns true and sets `result` if the
// conversion was successful, false if the integer is too big to fit the
// double range. If `rounding_direction` is not nullptr, it will be set to a
// value indicating what rounding occured.
static inline bool convertLargeIntToDouble(const LargeInt& large_int,
double* result,
RoundingDirection* rounding) {
// The following algorithm looks at the highest n bits of the integer and puts
// them into the mantissa of the floating point number. It extracts two
// extra bits to account for the highest bit not being explicitly encoded
// in floating point and the lowest bit to decide whether we should round
// up or down.
// Extract the highest two digits of the numbers magnitude.
word num_digits = large_int.numDigits();
DCHECK(num_digits > 1, "must have more than 1 digit");
uword high_digit = large_int.digitAt(num_digits - 1);
uword second_highest_digit = large_int.digitAt(num_digits - 2);
bool is_negative = large_int.isNegative();
uword carry_to_second_highest = 0;
if (is_negative) {
// The magnitude of a negative value is `~value + 1`. We compute the
// complement of the highest two digits and possibly add a carry.
carry_to_second_highest = 1;
for (word i = num_digits - 3; i >= 0; i--) {
// Any `digit != 0` will have a zero bit so we won't have a carry.
if (large_int.digitAt(i) != 0) {
carry_to_second_highest = 0;
break;
}
}
second_highest_digit = ~second_highest_digit + carry_to_second_highest;
uword carry_to_highest = second_highest_digit == 0 ? 1 : 0;
high_digit = ~high_digit + carry_to_highest;
// A negative number has the highest bit set so incrementing the complement
// cannot overflow.
DCHECK(carry_to_highest == 0 || high_digit != 0,
"highest digit cannot overflow");
}
// Determine the exponent bits.
int high_bit = Utils::highestBit(high_digit);
uword exponent_bits = kBitsPerDouble - kDoubleMantissaBits - 1;
uword exponent_bias = (1 << (exponent_bits - 1)) - 1;
uword exponent =
(num_digits - 1) * kBitsPerWord + high_bit - 1 + exponent_bias;
// Extract mantissa bits including the high bit which is implicit in the
// float representation and one extra bit to help determine if we need to
// round up.
// We also keep track if the bits shifted out on the right side are zero.
int shift = high_bit - (kDoubleMantissaBits + 2);
int shift_right = Utils::maximum(shift, 0);
int shift_left = -Utils::minimum(shift, 0);
uword value_as_word = (high_digit >> shift_right) << shift_left;
bool lesser_significant_bits_zero;
if (shift_left > 0) {
int lower_shift_right = kBitsPerWord - shift_left;
value_as_word |= second_highest_digit >> lower_shift_right;
lesser_significant_bits_zero = (second_highest_digit << shift_left) == 0;
} else {
lesser_significant_bits_zero =
second_highest_digit == 0 &&
(shift_right == 0 || (high_digit << (kBitsPerWord - shift_right)) == 0);
}
// Returns true if all digits (in the numbers magnitude) below the 2 highest
// digits are zero.
auto lower_bits_zero = [&]() -> bool {
if (!lesser_significant_bits_zero) return false;
// Already scanned the digits in the negative case and can look at carry.
if (is_negative) return carry_to_second_highest != 0;
for (word i = num_digits - 3; i >= 0; i--) {
if (large_int.digitAt(i) != 0) return false;
}
return true;
};
// We need to round down if the least significant bit is zero, we need to
// round up if the least significant and any other bit is one. If the
// least significant bit is one and all other bits are zero then we look at
// second least significant bit to round towards an even number.
if ((value_as_word & 0x3) == 0x3 ||
((value_as_word & 1) && !lower_bits_zero())) {
value_as_word++;
// This may have triggered an overflow, so we need to add 1 to the exponent.
if (value_as_word == (uword{1} << (kDoubleMantissaBits + 2))) {
exponent++;
}
if (rounding != nullptr) {
*rounding = RoundUp;
}
} else if (rounding != nullptr) {
*rounding =
!(value_as_word & 1) && lower_bits_zero() ? NoRounding : RoundDown;
}
value_as_word >>= 1;
// Check for overflow.
// The biggest exponent is used to mark special numbers like NAN or INF.
uword max_exponent = (1 << exponent_bits) - 1;
if (exponent > max_exponent - 1) {
return false;
}
// Mask out implicit bit, combine mantissa, exponent and sign.
value_as_word &= (uword{1} << kDoubleMantissaBits) - 1;
value_as_word |= exponent << kDoubleMantissaBits;
value_as_word |= uword{is_negative} << (kDoubleMantissaBits + exponent_bits);
*result = bit_cast<double>(value_as_word);
return true;
}
RawObject convertIntToDouble(Thread* thread, const Int& value, double* result) {
if (value.numDigits() == 1) {
*result = static_cast<double>(value.asWord());
return NoneType::object();
}
HandleScope scope(thread);
LargeInt large_int(&scope, *value);
if (!convertLargeIntToDouble(large_int, result, nullptr)) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"int too large to convert to float");
}
return NoneType::object();
}
bool compareDoubleWithInt(Thread* thread, double left, const Int& right,
CompareOp op) {
DCHECK(op == GE || op == GT || op == LE || op == LT, "needs inequality op");
bool compare_equal = op == LE || op == GE;
bool compare_less = op == LT || op == LE;
bool compare_greater = !compare_less;
if (!std::isfinite(left)) {
if (std::isnan(left)) return false;
DCHECK(std::isinf(left), "remaining case must be infinity");
return compare_less == (left < 0);
}
word num_digits = right.numDigits();
if (num_digits == 1) {
word right_word = right.asWord();
double right_double = static_cast<double>(right_word);
if (left < right_double) return compare_less;
if (left > right_double) return compare_greater;
// TODO(matthiasb): We could also detect the rounding direction by
// performing bit operations on `right_word` which is more complicated but
// may be faster; benchmark.
word right_double_word = static_cast<word>(right_double);
if (right_double_word == right_word) return compare_equal;
return compare_less == (right_double_word < right_word);
}
// Shortcut for differing signs.
if ((left < 0) != right.isNegative()) {
DCHECK((compare_less == (left < 0)) == (compare_greater == (left > 0)),
"conditions must be exclusive");
return compare_less == (left < 0);
}
double right_double;
RoundingDirection rounding;
HandleScope scope(thread);
LargeInt large_int(&scope, *right);
if (!convertLargeIntToDouble(large_int, &right_double, &rounding)) {
return compare_less != (left < 0);
}
if (left < right_double) return compare_less;
if (left > right_double) return compare_greater;
if (rounding == NoRounding) return compare_equal;
return compare_less == (rounding == RoundDown);
}
bool doubleEqualsInt(Thread* thread, double left, const Int& right) {
// This is basically the same code as `doubleCompareWithInt` but can take some
// shortcuts because we don't care about the lesser/greater situations.
word num_digits = right.numDigits();
if (num_digits == 1) {
word right_word = right.asWord();
double right_double = static_cast<double>(right_word);
if (left != right_double) return false;
// Check whether any rounding occured when converting to floating-point.
// TODO(matthiasb): We can also check this via bit operations on
// `right_word` which is more complicated but may be faster; should run
// some benchmarks.
return static_cast<word>(right_double) == right_word;
}
if (!std::isfinite(left)) {
return false;
}
double right_double;
RoundingDirection rounding;
HandleScope scope(thread);
LargeInt large_int(&scope, *right);
if (!convertLargeIntToDouble(large_int, &right_double, &rounding)) {
return false;
}
return rounding == NoRounding && left == right_double;
}
RawObject intFromIndex(Thread* thread, const Object& obj) {
Runtime* runtime = thread->runtime();
if (runtime->isInstanceOfInt(*obj)) {
return *obj;
}
HandleScope scope(thread);
Object result(&scope, thread->invokeMethod1(obj, ID(__index__)));
if (result.isError()) {
if (result.isErrorNotFound()) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"'%T' object cannot be interpreted as an integer", &obj);
}
return *result;
}
if (runtime->isInstanceOfInt(*result)) {
return *result;
}
return thread->raiseWithFmt(LayoutId::kTypeError,
"__index__ returned non-int (type %T)", &result);
}
} // namespace py