in runtime/float-builtins.cpp [889:1035]
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);
}