runtime/float-builtins.cpp (840 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "float-builtins.h" #include <cfloat> #include <cmath> #include <limits> #include "builtins.h" #include "float-conversion.h" #include "formatter.h" #include "frame.h" #include "globals.h" #include "int-builtins.h" #include "objects.h" #include "runtime.h" #include "thread.h" #include "type-builtins.h" #include "unicode.h" #include "utils.h" namespace py { // Convert `object` to double. // Returns a NoneType and sets `value` if the conversion was successful. // Returns an error or unimplemented otherwise. This does specifically not // look for `__float__` to match the behavior of `CONVERT_TO_DOUBLE()` in // cpython. static RawObject convertToDouble(Thread* thread, const Object& object, double* result) { Runtime* runtime = thread->runtime(); if (runtime->isInstanceOfFloat(*object)) { *result = floatUnderlying(*object).value(); return NoneType::object(); } if (runtime->isInstanceOfInt(*object)) { HandleScope scope(thread); Int value(&scope, intUnderlying(*object)); return convertIntToDouble(thread, value, result); } return NotImplementedType::object(); } static const BuiltinAttribute kUserFloatBaseAttributes[] = { {ID(_UserFloat__value), RawUserFloatBase::kValueOffset, AttributeFlags::kHidden}, }; void initializeFloatType(Thread* thread) { addBuiltinType(thread, ID(float), LayoutId::kFloat, /*superclass_id=*/LayoutId::kObject, kUserFloatBaseAttributes, UserFloatBase::kSize, /*basetype=*/true); } static void digitsFromDigitsWithUnderscores(const char* s, char* dup, word* length) { char* end = dup; char prev = '\0'; const char* p; const char* last = s + *length; for (p = s; p < last; p++) { if (*p == '_') { // Underscores are only allowed after digits. if (!ASCII::isDigit(prev)) { return; } } else { *end++ = *p; // Underscores are only allowed before digits. if (prev == '_' && !ASCII::isDigit(*p)) { return; } } prev = *p; } // Underscores are not allowed at the end. if (prev == '_') { return; } *end = '\0'; *length = end - dup; } RawObject floatFromDigits(Thread* thread, const char* str, word length) { // TODO(T57022841): follow full CPython conversion for strings char* end; char* new_str; const char* dup = str; word dup_length = length; bool release_memory = false; if (std::strchr(str, '_') != nullptr) { word* new_length = &dup_length; release_memory = true; new_str = new char[length + 1]; digitsFromDigitsWithUnderscores(str, new_str, new_length); if (new_str == nullptr) { delete[] new_str; return thread->raiseWithFmt(LayoutId::kValueError, "could not convert string to float: '%s'", str); } dup = new_str; } double result = std::strtod(dup, &end); // Overflow, return infinity or negative infinity. if (result == HUGE_VAL) { result = std::numeric_limits<double>::infinity(); } else if (result == -HUGE_VAL) { result = -std::numeric_limits<double>::infinity(); } else if (dup_length == 0 || end - dup != dup_length) { // Conversion was incomplete; the string was not a valid float. if (release_memory) { delete[] new_str; } return thread->raiseWithFmt(LayoutId::kValueError, "could not convert string to float: '%s'", str); } if (release_memory) { delete[] new_str; } return thread->runtime()->newFloat(result); } RawObject METH(float, __abs__)(Thread* thread, Arguments args) { Runtime* runtime = thread->runtime(); HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double self = floatUnderlying(*self_obj).value(); return runtime->newFloat(std::fabs(self)); } RawObject METH(float, __bool__)(Thread* thread, Arguments args) { Runtime* runtime = thread->runtime(); HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double self = floatUnderlying(*self_obj).value(); return Bool::fromBool(self != 0.0); } RawObject METH(float, __eq__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double left = floatUnderlying(*self).value(); Object right(&scope, args.get(1)); bool result; if (runtime->isInstanceOfFloat(*right)) { result = left == floatUnderlying(*right).value(); } else if (runtime->isInstanceOfInt(*right)) { Int right_int(&scope, intUnderlying(*right)); result = doubleEqualsInt(thread, left, right_int); } else { return NotImplementedType::object(); } return Bool::fromBool(result); } RawObject METH(float, __float__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } return floatUnderlying(*self); } RawObject METH(float, __format__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } Object spec_obj(&scope, args.get(1)); if (!runtime->isInstanceOfStr(*spec_obj)) { return thread->raiseRequiresType(spec_obj, ID(str)); } Float self(&scope, floatUnderlying(*self_obj)); Str spec(&scope, strUnderlying(*spec_obj)); if (spec == Str::empty()) { if (self_obj.isFloat()) { unique_c_ptr<char> result( doubleToString(self.value(), 'r', 0, false, true, false, nullptr)); return runtime->newStrFromCStr(result.get()); } Object str(&scope, thread->invokeMethod1(self_obj, ID(__str__))); DCHECK(!str.isErrorNotFound(), "__str__ should always exist"); if (str.isErrorException()) return *str; if (!runtime->isInstanceOfStr(*str)) { return thread->raiseWithFmt( LayoutId::kTypeError, "__str__ returned non-string (type %T)", &str); } return *str; } FormatSpec format; Object err(&scope, parseFormatSpec(thread, spec, '\0', '>', &format)); if (err.isErrorException()) { return *err; } switch (format.type) { case '\0': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'n': case '%': return formatFloat(thread, self.value(), &format); default: return raiseUnknownFormatError(thread, format.type, self_obj); } } RawObject METH(float, __ge__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double left = floatUnderlying(*self).value(); Object right(&scope, args.get(1)); bool result; if (runtime->isInstanceOfFloat(*right)) { result = left >= floatUnderlying(*right).value(); } else if (runtime->isInstanceOfInt(*right)) { Int right_int(&scope, intUnderlying(*right)); result = compareDoubleWithInt(thread, left, right_int, GE); } else { return NotImplementedType::object(); } return Bool::fromBool(result); } RawObject METH(float, __gt__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double left = floatUnderlying(*self).value(); Object right(&scope, args.get(1)); bool result; if (runtime->isInstanceOfFloat(*right)) { result = left > floatUnderlying(*right).value(); } else if (runtime->isInstanceOfInt(*right)) { Int right_int(&scope, intUnderlying(*right)); result = compareDoubleWithInt(thread, left, right_int, GT); } else { return NotImplementedType::object(); } return Bool::fromBool(result); } void decodeDouble(double value, bool* is_neg, int* exp, int64_t* mantissa) { const uint64_t man_mask = (uint64_t{1} << kDoubleMantissaBits) - 1; const int num_exp_bits = kBitsPerDouble - kDoubleMantissaBits - 1; const uint64_t exp_mask = (uint64_t{1} << num_exp_bits) - 1; const int exp_bias = (1 << (num_exp_bits - 1)) - 1; uint64_t value_bits = bit_cast<uint64_t>(value); *is_neg = value_bits >> (kBitsPerDouble - 1); *exp = ((value_bits >> kDoubleMantissaBits) & exp_mask) - exp_bias; *mantissa = value_bits & man_mask; } RawObject intFromDouble(Thread* thread, double value) { bool is_neg; int exp; int64_t man; decodeDouble(value, &is_neg, &exp, &man); const int exp_bits = kBitsPerDouble - kDoubleMantissaBits - 1; const int max_exp = 1 << (exp_bits - 1); if (exp == max_exp) { if (man == 0) { return thread->raiseWithFmt(LayoutId::kOverflowError, "cannot convert float infinity to integer"); } return thread->raiseWithFmt(LayoutId::kValueError, "cannot convert float NaN to integer"); } // No integral part. if (exp < 0) { return SmallInt::fromWord(0); } // Number of bits needed to represent the result integer in 2's complement. // +1 for the implicit bit of value 1 and +1 for the sign bit. int result_bits = exp + 2; // If the number is the negative number of the greatest magnitude // (-10000...b), then no extra sign bit is needed. if (is_neg && man == 0) { result_bits = exp + 1; } // Fast path for integers that are a word or smaller in size. const word man_with_implicit_one = (word{1} << kDoubleMantissaBits) | man; // Path that fills a digit of Int, and left-shifts it to match // its magnitude with the given exponent. DCHECK( man_with_implicit_one >= 0, "man_with_implicit_one must be positive before the sign bit is applied."); Runtime* runtime = thread->runtime(); if (result_bits <= kBitsPerWord) { const word result = (exp > kDoubleMantissaBits ? (man_with_implicit_one << (exp - kDoubleMantissaBits)) : (man_with_implicit_one >> (kDoubleMantissaBits - exp))); return runtime->newInt(is_neg ? -result : result); } // TODO(djang): Make another interface for intBinaryLshift() to accept // words directly. HandleScope scope(thread); Int unshifted_result(&scope, runtime->newInt(is_neg ? -man_with_implicit_one : man_with_implicit_one)); Int shifting_bits(&scope, runtime->newInt(exp - kDoubleMantissaBits)); return runtime->intBinaryLshift(thread, unshifted_result, shifting_bits); } word doubleHash(double value) { bool is_neg; int exp; int64_t mantissa; decodeDouble(value, &is_neg, &exp, &mantissa); const int exp_bits = kBitsPerDouble - kDoubleMantissaBits - 1; const int max_exp = 1 << (exp_bits - 1); const int min_exp = -(1 << (exp_bits - 1)) + 1; if (exp == max_exp) { word result; if (mantissa == 0) { result = is_neg ? -kHashInf : kHashInf; } else { result = kHashNan; } return result; } // The problem in the following is that for float numbers that compare equal // to an int number, the hash values have to equal the hash values produced // when hashing the integer. To achieve this we base the hashing on the same // ideas as `longIntHash()`. Here we want to compute // `(mantissa << (exp - mantissa_bits)) % kArithmeticHashModulus`. // `mantissa` is guaranteed to be smaller than `kArithmeticHashModulus` so as // explained in `longIntHash()` this just means we have to rotate it's bits by // `exp` for the result. // Add implicit one to mantissa if the number is not a subnormal. if (exp > min_exp) { mantissa |= int64_t{1} << kDoubleMantissaBits; } else if (mantissa == 0) { // Shortcut for 0.0 / -0.0. return 0; } else { // sub-normal number, adjust exponent. exp += 1; } // Compute `mantissa % kArithmeticHashModulus` which is just `mantissa`. static_assert(uword{1} << (kDoubleMantissaBits + 1) < kArithmeticHashModulus, "assumption `mantissa < modulus` does not hold"); uword result = mantissa; // `mantissa` represented `kDoubleMantissaBits` bits shifted by `exp`. We want // to align the first integral bit to bit 0 in the result, so we have to // rotate by `exp - kDoubleMantissaBits`. exp -= kDoubleMantissaBits; exp = exp >= 0 ? exp % kArithmeticHashBits : kArithmeticHashBits - 1 - ((-1 - exp) % kArithmeticHashBits); result = ((result << exp) & kArithmeticHashModulus) | result >> (kArithmeticHashBits - exp); if (is_neg) { 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--; } // Note: We cannot cache the hash value in the object header, because the // result must correspond to the hash values of SmallInt/LargeInt. The object // header however has fewer bits and can only store non-negative hash codes. return static_cast<word>(result); } RawObject METH(float, __hash__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double self = floatUnderlying(*self_obj).value(); return SmallInt::fromWord(doubleHash(self)); } RawObject METH(float, __int__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double self = floatUnderlying(*self_obj).value(); return intFromDouble(thread, self); } RawObject METH(float, __le__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double left = floatUnderlying(*self).value(); Object right(&scope, args.get(1)); bool result; if (runtime->isInstanceOfFloat(*right)) { result = left <= floatUnderlying(*right).value(); } else if (runtime->isInstanceOfInt(*right)) { Int right_int(&scope, intUnderlying(*right)); result = compareDoubleWithInt(thread, left, right_int, LE); } else { return NotImplementedType::object(); } return Bool::fromBool(result); } RawObject METH(float, __lt__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double left = floatUnderlying(*self).value(); Object right(&scope, args.get(1)); bool result; if (runtime->isInstanceOfFloat(*right)) { result = left < floatUnderlying(*right).value(); } else if (runtime->isInstanceOfInt(*right)) { Int right_int(&scope, intUnderlying(*right)); result = compareDoubleWithInt(thread, left, right_int, LT); } else { return NotImplementedType::object(); } return Bool::fromBool(result); } RawObject METH(float, __mul__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double left = floatUnderlying(*self_obj).value(); double right; Object other(&scope, args.get(1)); Object maybe_error(&scope, convertToDouble(thread, other, &right)); // May have returned NotImplemented or raised an exception. if (!maybe_error.isNoneType()) return *maybe_error; return runtime->newFloat(left * right); } RawObject METH(float, __neg__)(Thread* thread, Arguments args) { Runtime* runtime = thread->runtime(); HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double self = floatUnderlying(*self_obj).value(); return runtime->newFloat(-self); } RawObject METH(float, __add__)(Thread* thread, Arguments args) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object self(&scope, args.get(0)); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double left = floatUnderlying(*self).value(); double right; Object other(&scope, args.get(1)); Object maybe_error(&scope, convertToDouble(thread, other, &right)); // May have returned NotImplemented or raised an exception. if (!maybe_error.isNoneType()) return *maybe_error; return runtime->newFloat(left + right); } RawObject METH(float, __truediv__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double left = floatUnderlying(*self_obj).value(); double right; Object other(&scope, args.get(1)); Object maybe_error(&scope, convertToDouble(thread, other, &right)); // May have returned NotImplemented or raised an exception. if (!maybe_error.isNoneType()) return *maybe_error; if (right == 0.0) { return thread->raiseWithFmt(LayoutId::kZeroDivisionError, "float division by zero"); } return runtime->newFloat(left / right); } RawObject METH(float, __round__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } Float value_float(&scope, floatUnderlying(*self_obj)); double value = value_float.value(); // If ndigits is None round to nearest integer. Object ndigits_obj(&scope, args.get(1)); if (ndigits_obj.isNoneType()) { double result = std::round(value); // round to even. if (std::fabs(value - result) == 0.5) { result = 2.0 * std::round(value / 2.0); } return intFromDouble(thread, result); } // Round to ndigits decimals. if (!runtime->isInstanceOfInt(*ndigits_obj)) { return thread->raiseWithFmt(LayoutId::kTypeError, "'%T' cannot be interpreted as an integer", &ndigits_obj); } Int ndigits_int(&scope, intUnderlying(*ndigits_obj)); if (ndigits_int.isLargeInt()) { return ndigits_int.isNegative() ? runtime->newFloat(0.0) : *value_float; } word ndigits = ndigits_int.asWord(); // Keep NaNs and infinities unchanged. if (!std::isfinite(value)) { return *value_float; } // Set some reasonable bounds on ndigits and clip otherwise. // For `ndigits > ndigits_max`, `value` always rounds to itself. // For `ndigits < ndigits_min`, `value` always rounds to +-0.0. // Here 0.30103 is an upper bound for `log10(2)`. static const word ndigits_max = static_cast<word>((kDoubleDigits - kDoubleMinExp) * 0.30103); static const word ndigits_min = -static_cast<word>((kDoubleMaxExp + 1) * 0.30103); if (ndigits > ndigits_max) { return *value_float; } double result; if (ndigits < ndigits_min) { result = std::copysign(0.0, value); } else { result = doubleRoundDecimals(value, static_cast<int>(ndigits)); if (result == HUGE_VAL || result == -HUGE_VAL) { return thread->raiseWithFmt(LayoutId::kOverflowError, "rounded value too large to represent"); } } return runtime->newFloat(result); } RawObject METH(float, __rtruediv__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double right = floatUnderlying(*self_obj).value(); double left; Object other(&scope, args.get(1)); Object maybe_error(&scope, convertToDouble(thread, other, &left)); // May have returned NotImplemented or raised an exception. if (!maybe_error.isNoneType()) return *maybe_error; if (right == 0.0) { return thread->raiseWithFmt(LayoutId::kZeroDivisionError, "float division by zero"); } return runtime->newFloat(left / right); } RawObject METH(float, __sub__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double left = floatUnderlying(*self).value(); double right; Object other(&scope, args.get(1)); Object maybe_error(&scope, convertToDouble(thread, other, &right)); // May have returned NotImplemented or raised an exception. if (!maybe_error.isNoneType()) return *maybe_error; return runtime->newFloat(left - right); } RawObject METH(float, __trunc__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } double self = floatUnderlying(*self_obj).value(); double integral_part; static_cast<void>(std::modf(self, &integral_part)); return intFromDouble(thread, integral_part); } RawObject METH(float, __pow__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } if (!args.get(2).isNoneType()) { return thread->raiseWithFmt( LayoutId::kTypeError, "pow() 3rd argument not allowed unless all arguments are integers"); } double left = floatUnderlying(*self).value(); double right; Object other(&scope, args.get(1)); Object maybe_error(&scope, convertToDouble(thread, other, &right)); // May have returned NotImplemented or raised an exception. if (!maybe_error.isNoneType()) return *maybe_error; return runtime->newFloat(std::pow(left, right)); } static word nextNonHexDigit(const Str& str, word pos) { for (word len = str.length(); pos < len; ++pos) { if (!Byte::isHexDigit(str.byteAt(pos))) { break; } } return pos; } static word nextNonWhitespace(const Str& str, word pos) { for (word length = str.length(); pos < length; ++pos) { if (!Byte::isSpace(str.byteAt(pos))) { break; } } return pos; } static bool strParseOptionalSign(const Str& str, word* pos) { if (*pos >= str.length()) { return false; } switch (str.byteAt(*pos)) { case '-': ++(*pos); return true; case '+': ++(*pos); // FALLTHROUGH default: return false; } } static bool strAdvancePrefixCaseInsensitiveASCII(const Str& str, word* pos, const char* lowercase_prefix) { // Caution: if supporting unicode, don't re-write this naively, // string case operations are tricky, and locale/language // dependent DCHECK(pos != nullptr, "pos must be non-null"); DCHECK(lowercase_prefix != nullptr, "lowercase_prefix must be non-null"); word i = *pos; word length = str.length(); if (i >= length) { return false; } for (; i < length && *lowercase_prefix != '\0'; ++i, ++lowercase_prefix) { if (Byte::toLower(str.byteAt(i)) != *lowercase_prefix) { return false; } } // Ensure that the entire prefix was present bool result = *lowercase_prefix == '\0'; if (result) { *pos = i; } return result; } static bool parseInfOrNan(const Str& str, word* pos, double* result) { word pos_start = *pos; bool negate = strParseOptionalSign(str, pos); if (strAdvancePrefixCaseInsensitiveASCII(str, pos, "inf")) { strAdvancePrefixCaseInsensitiveASCII(str, pos, "inity"); *result = negate ? -kDoubleInfinity : kDoubleInfinity; } else if (strAdvancePrefixCaseInsensitiveASCII(str, pos, "nan")) { *result = negate ? -kDoubleNaN : kDoubleNaN; } else { *pos = pos_start; *result = -1.0; return false; } return true; } static RawObject newFloatOrSubclass(Thread* thread, const Type& type, const Str& str, word pos, double result) { // Optional trailing whitespace leading to the end of the string pos = nextNonWhitespace(str, pos); if (pos != str.length()) { return thread->raiseWithFmt(LayoutId::kValueError, "invalid hexadecimal floating-point string"); } if (type.instanceLayoutId() == LayoutId::kFloat) { return thread->runtime()->newFloat(result); } HandleScope scope(thread); Object obj(&scope, thread->runtime()->newFloat(result)); return Interpreter::call1(thread, type, obj); } // For 0 <= i < ndigits (implicit), get_hex_digit(i) gives the jth most // significant digit, skipping over the '.' between integral and fractional // digits. static word getHexDigit(const Str& str, word fdigits, word coeff_end, word i) { // Note: this assumes that: // A) all hex digit codepoints have been previously verified to be 1 byte long // B) the separating 'p' or 'P' character has been previously verified to be 1 // byte long. word pos = i < fdigits ? coeff_end - (i) : coeff_end - 1 - i; word result = Byte::toHexDigit(str.byteAt(pos)); DCHECK(result >= 0, "Only hex digits should be indexed here"); return result; } // Computes a integer float value from the digits in str via getHexDigit(), from // digit_ms..digit_ls, inclusive static double sumHexDigitsDouble(const Str& str, word fdigits, word coeff_end, word digit_ms, word digit_ls) { double result = 0; for (word i = digit_ms; i >= digit_ls; --i) { result = 16.0 * result + getHexDigit(str, fdigits, coeff_end, i); } return result; } static RawObject raiseOverflowErrorHexFloatTooLarge(Thread* thread) { return thread->raiseWithFmt( LayoutId::kOverflowError, "hexadecimal value too large to represent as a float"); } static void floatHexParseCoefficients(const Str& str, word* pos, word* ndigits, word* fdigits, word* coeff_end) { DCHECK(pos != nullptr && ndigits != nullptr && fdigits != nullptr && coeff_end != nullptr, "Invalid argument to floatHexParseCoefficients"); word coeff_start = *pos; *pos = nextNonHexDigit(str, *pos); const word length = str.length(); word pos_store = *pos; if (*pos < length && '.' == str.byteAt(*pos)) { // Note skipping over the '.' character *pos = nextNonHexDigit(str, *pos + 1); *coeff_end = *pos - 1; } else { *coeff_end = *pos; } // ndigits = total # of hex digits; fdigits = # after point *ndigits = *coeff_end - coeff_start; *fdigits = *coeff_end - pos_store; } static long floatHexParseExponent(const Str& str, word* pos) { long exponent = 0; if (*pos < str.length() && 'p' == Byte::toLower(str.byteAt(*pos))) { ++(*pos); bool negate = strParseOptionalSign(str, pos); for (word length = str.length(); *pos < length && Byte::isDigit(str.byteAt(*pos)); ++(*pos)) { exponent = exponent * 10 + Byte::toDigit(str.byteAt(*pos)); } if (negate) { exponent = -exponent; } } return exponent; } RawObject METH(float, as_integer_ratio)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(float)); } Float self(&scope, floatUnderlying(*self_obj)); double value = self.value(); if (std::isinf(value)) { return thread->raiseWithFmt(LayoutId::kOverflowError, "cannot convert Infinity to integer ratio"); } if (std::isnan(value)) { return thread->raiseWithFmt(LayoutId::kValueError, "cannot convert NaN to integer ratio"); } int exponent; double float_part = std::frexp(value, &exponent); // If FLT_RADIX != 2, the 300 steps may leave a tiny fractional part to be // truncated by intFromDouble. for (word i = 0; i < 300 && float_part != std::floor(float_part); i++) { float_part *= 2.0; exponent--; } Object numerator_obj(&scope, intFromDouble(thread, float_part)); if (numerator_obj.isErrorException()) { return *numerator_obj; } Int numerator(&scope, *numerator_obj); Int denominator(&scope, SmallInt::fromWord(1)); if (exponent > 0) { Int exponent_obj(&scope, SmallInt::fromWord(exponent)); numerator = runtime->intBinaryLshift(thread, numerator, exponent_obj); } else { Int exponent_obj(&scope, SmallInt::fromWord(-exponent)); denominator = runtime->intBinaryLshift(thread, denominator, exponent_obj); } return runtime->newTupleWith2(numerator, denominator); } RawObject METH(float, fromhex)(Thread* thread, Arguments args) { // Convert a hexadecimal string to a float. // Check the function arguments HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object type_obj(&scope, args.get(0)); if (!runtime->isInstanceOfType(*type_obj)) { return thread->raiseRequiresType(type_obj, ID(type)); } Type type(&scope, *type_obj); Object str_obj(&scope, args.get(1)); if (!runtime->isInstanceOfStr(*str_obj)) { return thread->raiseRequiresType(str_obj, ID(str)); } const Str str(&scope, strUnderlying(*str_obj)); // // Parse the string // // leading whitespace word pos = nextNonWhitespace(str, 0); // infinities and nans { double result; if (parseInfOrNan(str, &pos, &result)) { return newFloatOrSubclass(thread, type, str, pos, result); } } // optional sign bool negate = strParseOptionalSign(str, &pos); // [0x] strAdvancePrefixCaseInsensitiveASCII(str, &pos, "0x"); // coefficient: <integer> [. <fraction>] word ndigits, fdigits, coeff_end; floatHexParseCoefficients(str, &pos, &ndigits, &fdigits, &coeff_end); if (ndigits == 0) { return thread->raiseWithFmt( LayoutId::kValueError, "invalid hexadecimal floating-point string, no digits"); } if (ndigits > Utils::minimum(kDoubleMinExp - kDoubleDigits - kMinLong / 2, kMaxLong / 2 + 1 - kDoubleMaxExp) / 4) { return thread->raiseWithFmt(LayoutId::kValueError, "hexadecimal string too long to convert"); } // [p <exponent>] long exponent = floatHexParseExponent(str, &pos); // // Compute rounded value of the hex string // // Discard leading zeros, and catch extreme overflow and underflow while (ndigits > 0 && getHexDigit(str, fdigits, coeff_end, ndigits - 1) == 0) { --ndigits; } if (ndigits == 0 || exponent < kMinLong / 2) { return newFloatOrSubclass(thread, type, str, pos, negate ? -0.0 : 0.0); } if (exponent > kMaxLong / 2) { return raiseOverflowErrorHexFloatTooLarge(thread); } // Adjust exponent for fractional part, 4 bits per nibble exponent -= 4 * static_cast<long>(fdigits); // top_exponent = 1 more than exponent of most sig. bit of coefficient long top_exponent = exponent + 4 * (static_cast<long>(ndigits) - 1); for (int digit = getHexDigit(str, fdigits, coeff_end, ndigits - 1); digit != 0; digit /= 2) { ++top_exponent; } // catch almost all nonextreme cases of overflow and underflow here if (top_exponent < kDoubleMinExp - kDoubleDigits) { return newFloatOrSubclass(thread, type, str, pos, negate ? -0.0 : 0.0); } if (top_exponent > kDoubleMaxExp) { return raiseOverflowErrorHexFloatTooLarge(thread); } // lsb = exponent of least significant bit of the *rounded* value. // This is top_exponent - kDoubleDigits unless result is subnormal. long lsb = Utils::maximum(top_exponent, static_cast<long>(kDoubleMinExp)) - kDoubleDigits; // Check if rounding required double result = 0.0; if (exponent >= lsb) { // no rounding required result = sumHexDigitsDouble(str, fdigits, coeff_end, ndigits - 1, 0); } else { // rounding required. key_digit is the index of the hex digit // containing the first bit to be rounded away. int half_eps = 1 << static_cast<int>((lsb - exponent - 1) % 4); long key_digit = (lsb - exponent - 1) / 4; result = sumHexDigitsDouble(str, fdigits, coeff_end, ndigits - 1, key_digit + 1); // sum in the final key_digit, but subtract off 2*half_eps from it first to // allow for the rounding below. int digit = getHexDigit(str, fdigits, coeff_end, key_digit); result = 16.0 * result + static_cast<double>(digit & (16 - 2 * half_eps)); // round-half-even: round up if bit lsb-1 is 1 and at least one of // bits lsb, lsb-2, lsb-3, lsb-4, ... is 1. if ((digit & half_eps) != 0) { bool round_up = false; if ((digit & (3 * half_eps - 1)) != 0 || (half_eps == 8 && (getHexDigit(str, fdigits, coeff_end, key_digit + 1) & 1) != 0)) { round_up = true; } else { for (ssize_t i = key_digit - 1; i >= 0; --i) { if (getHexDigit(str, fdigits, coeff_end, i) != 0) { round_up = true; break; } } } if (round_up) { result += 2 * half_eps; if (top_exponent == kDoubleMaxExp && result == ldexp(static_cast<double>(2 * half_eps), kDoubleDigits)) { // overflow corner case: pre-rounded value < 2**kDoubleMaxExp; // rounded=2**kDoubleMaxExp. return raiseOverflowErrorHexFloatTooLarge(thread); } } } // Adjust the exponent over 4 bits for every nibble we skipped processing exponent += 4 * key_digit; } result = ldexp(result, static_cast<int>(exponent)); return newFloatOrSubclass(thread, type, str, pos, negate ? -result : result); } RawObject METH(float, hex)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); Runtime* runtime = thread->runtime(); if (!runtime->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double double_value = floatUnderlying(*self).value(); return formatDoubleHexadecimalSimple(runtime, double_value); } RawObject METH(float, is_integer)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfFloat(*self)) { return thread->raiseRequiresType(self, ID(float)); } double double_value = floatUnderlying(*self).value(); return Bool::fromBool(!std::isinf(double_value) && std::floor(double_value) == double_value); } } // namespace py