* carefully work with types in the library.

* fix for some types errors.
* fix small amount of not optimized code.
* add more comments to the code.
* unified of function binary_format::max_mantissa_fast_path() because it's do the same.
This commit is contained in:
IRainman 2025-04-12 17:06:38 +03:00
parent 69fbbff062
commit ba1344c030
5 changed files with 225 additions and 224 deletions

View File

@ -261,8 +261,8 @@ enum class parse_error {
template <typename UC> struct parsed_number_string_t { template <typename UC> struct parsed_number_string_t {
// an unsigned int avoids signed overflows (which are bad) // an unsigned int avoids signed overflows (which are bad)
uint64_t mantissa{0}; am_mant_t mantissa{0};
int16_t exponent{0}; am_pow_t exponent{0};
UC const *lastmatch{nullptr}; UC const *lastmatch{nullptr};
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bool negative{false}; bool negative{false};
@ -334,13 +334,13 @@ parse_number_string(UC const *p, UC const *pend,
// multiplication // multiplication
answer.mantissa = answer.mantissa =
10 * answer.mantissa + 10 * answer.mantissa +
uint64_t(*p - UC(*p -
UC('0')); // might overflow, we will handle the overflow later UC('0')); // might overflow, we will handle the overflow later
++p; ++p;
} }
UC const *const end_of_integer_part = p; UC const *const end_of_integer_part = p;
uint16_t digit_count = am_digits digit_count =
static_cast<uint16_t>(end_of_integer_part - start_digits); static_cast<am_digits>(end_of_integer_part - start_digits);
answer.integer = span<UC const>(start_digits, digit_count); answer.integer = span<UC const>(start_digits, digit_count);
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
@ -364,14 +364,14 @@ parse_number_string(UC const *p, UC const *pend,
loop_parse_if_eight_digits(p, pend, answer.mantissa); loop_parse_if_eight_digits(p, pend, answer.mantissa);
while ((p != pend) && is_integer(*p)) { while ((p != pend) && is_integer(*p)) {
uint8_t const digit = uint8_t(*p - UC('0')); UC const digit = UC(*p - UC('0'));
answer.mantissa = answer.mantissa =
answer.mantissa * 10 + answer.mantissa * 10 +
digit; // in rare cases, this will overflow, but that's ok digit; // in rare cases, this will overflow, but that's ok
++p; ++p;
} }
answer.exponent = static_cast<int16_t>(before - p); answer.exponent = static_cast<am_pow_t>(before - p);
answer.fraction = span<UC const>(before, static_cast<uint16_t>(p - before)); answer.fraction = span<UC const>(before, static_cast<am_digits>(p - before));
digit_count -= answer.exponent; digit_count -= answer.exponent;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
@ -389,7 +389,7 @@ parse_number_string(UC const *p, UC const *pend,
// We have now parsed the integer and the fraction part of the mantissa. // We have now parsed the integer and the fraction part of the mantissa.
// Now we can parse the explicit exponential part. // Now we can parse the explicit exponential part.
int16_t exp_number = 0; // explicit exponential part am_pow_t exp_number = 0; // explicit exponential part
if ((p != pend) && (chars_format_t(options.format & chars_format::scientific) && if ((p != pend) && (chars_format_t(options.format & chars_format::scientific) &&
(UC('e') == *p) || (UC('e') == *p) ||
(UC('E') == *p)) (UC('E') == *p))
@ -431,7 +431,7 @@ parse_number_string(UC const *p, UC const *pend,
while ((p != pend) && is_integer(*p)) { while ((p != pend) && is_integer(*p)) {
if (exp_number < 0x1000) { if (exp_number < 0x1000) {
// check for exponent overflow if we have too many digits. // check for exponent overflow if we have too many digits.
uint8_t const digit = uint8_t(*p - UC('0')); UC const digit = UC(*p - UC('0'));
exp_number = 10 * exp_number + digit; exp_number = 10 * exp_number + digit;
} }
++p; ++p;
@ -478,24 +478,24 @@ parse_number_string(UC const *p, UC const *pend,
answer.mantissa = 0; answer.mantissa = 0;
p = answer.integer.ptr; p = answer.integer.ptr;
UC const *int_end = p + answer.integer.len(); UC const *int_end = p + answer.integer.len();
uint64_t const minimal_nineteen_digit_integer{1000000000000000000}; am_mant_t const minimal_nineteen_digit_integer{1000000000000000000};
while ((answer.mantissa < minimal_nineteen_digit_integer) && while ((answer.mantissa < minimal_nineteen_digit_integer) &&
(p != int_end)) { (p != int_end)) {
answer.mantissa = answer.mantissa * 10 + uint64_t(*p - UC('0')); answer.mantissa = answer.mantissa * 10 + UC(*p - UC('0'));
++p; ++p;
} }
if (answer.mantissa >= if (answer.mantissa >=
minimal_nineteen_digit_integer) { // We have a big integers minimal_nineteen_digit_integer) { // We have a big integers
answer.exponent = int16_t(end_of_integer_part - p) + exp_number; answer.exponent = am_pow_t(end_of_integer_part - p) + exp_number;
} else { // We have a value with a fractional component. } else { // We have a value with a fractional component.
p = answer.fraction.ptr; p = answer.fraction.ptr;
UC const *frac_end = p + answer.fraction.len(); UC const *frac_end = p + answer.fraction.len();
while ((answer.mantissa < minimal_nineteen_digit_integer) && while ((answer.mantissa < minimal_nineteen_digit_integer) &&
(p != frac_end)) { (p != frac_end)) {
answer.mantissa = answer.mantissa * 10 + uint64_t(*p - UC('0')); answer.mantissa = answer.mantissa * 10 + UC(*p - UC('0'));
++p; ++p;
} }
answer.exponent = int16_t(answer.fraction.ptr - p) + exp_number; answer.exponent = am_pow_t(answer.fraction.ptr - p) + exp_number;
} }
// We have now corrected both exponent and mantissa, to a truncated value // We have now corrected both exponent and mantissa, to a truncated value
} }

View File

@ -19,11 +19,11 @@ namespace fast_float {
#if defined(FASTFLOAT_64BIT) && !defined(__sparc) #if defined(FASTFLOAT_64BIT) && !defined(__sparc)
#define FASTFLOAT_64BIT_LIMB 1 #define FASTFLOAT_64BIT_LIMB 1
typedef uint64_t limb; typedef uint64_t limb;
constexpr uint8_t limb_bits = 64; constexpr limb_t limb_bits = 64;
#else #else
#define FASTFLOAT_32BIT_LIMB #define FASTFLOAT_32BIT_LIMB
typedef uint32_t limb; typedef uint32_t limb;
constexpr uint8_t limb_bits = 32; constexpr limb_t limb_bits = 32;
#endif #endif
typedef span<limb> limb_span; typedef span<limb> limb_span;
@ -32,12 +32,13 @@ typedef span<limb> limb_span;
// of bits required to store the largest bigint, which is // of bits required to store the largest bigint, which is
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
// ~3600 bits, so we round to 4000. // ~3600 bits, so we round to 4000.
constexpr uint16_t bigint_bits = 4000; typedef uint16_t bigint_bits_t;
constexpr uint8_t bigint_limbs = bigint_bits / limb_bits; constexpr bigint_bits_t bigint_bits = 4000;
constexpr limb_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire // vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes. // buffer is pre-allocated, and only the length changes.
template <uint8_t size> struct stackvec { template <limb_t size> struct stackvec {
limb data[size]; limb data[size];
// we never need more than 150 limbs // we never need more than 150 limbs
uint8_t length{0}; uint8_t length{0};
@ -53,31 +54,31 @@ template <uint8_t size> struct stackvec {
FASTFLOAT_ASSERT(try_extend(s)); FASTFLOAT_ASSERT(try_extend(s));
} }
FASTFLOAT_CONSTEXPR14 limb &operator[](uint16_t index) noexcept { FASTFLOAT_CONSTEXPR14 limb &operator[](limb_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
FASTFLOAT_CONSTEXPR14 const limb &operator[](uint16_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb &operator[](limb_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
// index from the end of the container // index from the end of the container
FASTFLOAT_CONSTEXPR14 const limb &rindex(uint16_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb &rindex(limb_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
uint16_t rindex = length - index - 1; limb_t rindex = length - index - 1;
return data[rindex]; return data[rindex];
} }
// set the length, without bounds checking. // set the length, without bounds checking.
FASTFLOAT_CONSTEXPR14 void set_len(uint8_t len) noexcept { length = len; } FASTFLOAT_CONSTEXPR14 void set_len(limb_t len) noexcept { length = len; }
constexpr uint8_t len() const noexcept { return length; } constexpr limb_t len() const noexcept { return length; }
constexpr bool is_empty() const noexcept { return length == 0; } constexpr bool is_empty() const noexcept { return length == 0; }
constexpr uint8_t capacity() const noexcept { return size; } constexpr limb_t capacity() const noexcept { return size; }
// append item to vector, without bounds checking // append item to vector, without bounds checking
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept { FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
@ -99,7 +100,7 @@ template <uint8_t size> struct stackvec {
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept { FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb *ptr = data + length; limb *ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr); std::copy_n(s.ptr, s.len(), ptr);
set_len(uint8_t(len() + s.len())); set_len(limb_t(len() + s.len()));
} }
// try to add items to the vector, returning if items were added // try to add items to the vector, returning if items were added
@ -116,32 +117,29 @@ template <uint8_t size> struct stackvec {
// if the new size is longer than the vector, assign value to each // if the new size is longer than the vector, assign value to each
// appended item. // appended item.
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
void resize_unchecked(uint8_t new_len, limb value) noexcept { void resize_unchecked(limb_t new_len, limb value) noexcept {
if (new_len > len()) { if (new_len > len()) {
uint8_t count = new_len - len(); limb_t count = new_len - len();
limb *first = data + len(); limb *first = data + len();
limb *last = first + count; limb *last = first + count;
::std::fill(first, last, value); ::std::fill(first, last, value);
set_len(new_len);
} else {
set_len(new_len);
} }
set_len(new_len);
} }
// try to resize the vector, returning if the vector was resized. // try to resize the vector, returning if the vector was resized.
FASTFLOAT_CONSTEXPR20 bool try_resize(uint8_t new_len, limb value) noexcept { FASTFLOAT_CONSTEXPR20 bool try_resize(limb_t new_len, limb value) noexcept {
if (new_len > capacity()) { if (new_len > capacity()) {
return false; return false;
} else { }
resize_unchecked(new_len, value); resize_unchecked(new_len, value);
return true; return true;
} }
}
// check if any limbs are non-zero after the given index. // check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index // this needs to be done in reverse order, since the index
// is relative to the most significant limbs. // is relative to the most significant limbs.
FASTFLOAT_CONSTEXPR14 bool nonzero(uint16_t index) const noexcept { FASTFLOAT_CONSTEXPR14 bool nonzero(limb_t index) const noexcept {
while (index < len()) { while (index < len()) {
if (rindex(index) != 0) { if (rindex(index) != 0) {
return true; return true;
@ -256,16 +254,15 @@ scalar_mul(limb x, limb y, limb &carry) noexcept {
// add scalar value to bigint starting from offset. // add scalar value to bigint starting from offset.
// used in grade school multiplication // used in grade school multiplication
template <uint8_t size> template <limb_t size>
inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y, inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
uint32_t start) noexcept { limb_t start) noexcept {
uint8_t index = (uint8_t)start;
limb carry = y; limb carry = y;
bool overflow; bool overflow;
while (carry != 0 && index < vec.len()) { while (carry != 0 && start < vec.len()) {
vec[index] = scalar_add(vec[index], carry, overflow); vec[start] = scalar_add(vec[start], carry, overflow);
carry = limb(overflow); carry = limb(overflow);
++index; ++start;
} }
if (carry != 0) { if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry)); FASTFLOAT_TRY(vec.try_push(carry));
@ -274,18 +271,18 @@ inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
} }
// add scalar value to bigint. // add scalar value to bigint.
template <uint8_t size> template <limb_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
small_add(stackvec<size> &vec, limb y) noexcept { small_add(stackvec<size> &vec, limb y) noexcept {
return small_add_from(vec, y, 0); return small_add_from(vec, y, 0);
} }
// multiply bigint by scalar value. // multiply bigint by scalar value.
template <uint8_t size> template <limb_t size>
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec, inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
limb y) noexcept { limb y) noexcept {
limb carry = 0; limb carry = 0;
for (uint8_t index = 0; index != vec.len(); ++index) { for (limb_t index = 0; index != vec.len(); ++index) {
vec[index] = scalar_mul(vec[index], y, carry); vec[index] = scalar_mul(vec[index], y, carry);
} }
if (carry != 0) { if (carry != 0) {
@ -296,17 +293,17 @@ inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
// add bigint to bigint starting from index. // add bigint to bigint starting from index.
// used in grade school multiplication // used in grade school multiplication
template <uint8_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y, FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
uint8_t start) noexcept { limb_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early // the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range. // if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) { if (x.len() < start || y.len() > x.len() - start) {
FASTFLOAT_TRY(x.try_resize(uint8_t(y.len() + start), 0)); FASTFLOAT_TRY(x.try_resize(limb_t(y.len() + start), 0));
} }
bool carry = false; bool carry = false;
for (uint8_t index = 0; index < y.len(); ++index) { for (limb_t index = 0; index < y.len(); ++index) {
limb xi = x[index + start]; limb xi = x[index + start];
limb yi = y[index]; limb yi = y[index];
bool c1 = false; bool c1 = false;
@ -321,20 +318,20 @@ FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
// handle overflow // handle overflow
if (carry) { if (carry) {
FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); FASTFLOAT_TRY(small_add_from(x, 1, limb_t(y.len() + start)));
} }
return true; return true;
} }
// add bigint to bigint. // add bigint to bigint.
template <uint8_t size> template <limb_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
large_add_from(stackvec<size> &x, limb_span y) noexcept { large_add_from(stackvec<size> &x, limb_span y) noexcept {
return large_add_from(x, y, 0); return large_add_from(x, y, 0);
} }
// grade-school multiplication algorithm // grade-school multiplication algorithm
template <uint8_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept { FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len()); limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs); stackvec<size> z(xs);
@ -343,7 +340,7 @@ FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
if (y.len() != 0) { if (y.len() != 0) {
limb y0 = y[0]; limb y0 = y[0];
FASTFLOAT_TRY(small_mul(x, y0)); FASTFLOAT_TRY(small_mul(x, y0));
for (uint8_t index = 1; index != y.len(); ++index) { for (limb_t index = 1; index != y.len(); ++index) {
limb yi = y[index]; limb yi = y[index];
stackvec<size> zi; stackvec<size> zi;
if (yi != 0) { if (yi != 0) {
@ -362,7 +359,7 @@ FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
} }
// grade-school multiplication algorithm // grade-school multiplication algorithm
template <uint8_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept { FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
if (y.len() == 1) { if (y.len() == 1) {
FASTFLOAT_TRY(small_mul(x, y[0])); FASTFLOAT_TRY(small_mul(x, y[0]));
@ -491,7 +488,7 @@ struct bigint : pow5_tables<> {
} else if (vec.len() < other.vec.len()) { } else if (vec.len() < other.vec.len()) {
return -1; return -1;
} else { } else {
for (uint8_t index = vec.len(); index > 0; --index) { for (limb_t index = vec.len(); index != 0; --index) {
limb xi = vec[index - 1]; limb xi = vec[index - 1];
limb yi = other.vec[index - 1]; limb yi = other.vec[index - 1];
if (xi > yi) { if (xi > yi) {
@ -506,7 +503,7 @@ struct bigint : pow5_tables<> {
// shift left each limb n bits, carrying over to the new limb // shift left each limb n bits, carrying over to the new limb
// returns true if we were able to shift all the digits. // returns true if we were able to shift all the digits.
FASTFLOAT_CONSTEXPR20 bool shl_bits(uint16_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl_bits(bigint_bits_t n) noexcept {
// Internally, for each item, we shift left by n, and add the previous // Internally, for each item, we shift left by n, and add the previous
// right shifted limb-bits. // right shifted limb-bits.
// For example, we transform (for u8) shifted left 2, to: // For example, we transform (for u8) shifted left 2, to:
@ -515,10 +512,10 @@ struct bigint : pow5_tables<> {
FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n != 0);
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
uint16_t const shl = n; bigint_bits_t const shl = n;
uint16_t const shr = limb_bits - shl; bigint_bits_t const shr = limb_bits - shl;
limb prev = 0; limb prev = 0;
for (uint8_t index = 0; index != vec.len(); ++index) { for (limb_t index = 0; index != vec.len(); ++index) {
limb xi = vec[index]; limb xi = vec[index];
vec[index] = (xi << shl) | (prev >> shr); vec[index] = (xi << shl) | (prev >> shr);
prev = xi; prev = xi;
@ -532,11 +529,16 @@ struct bigint : pow5_tables<> {
} }
// move the limbs left by `n` limbs. // move the limbs left by `n` limbs.
FASTFLOAT_CONSTEXPR20 bool shl_limbs(int16_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl_limbs(bigint_bits_t n) noexcept {
FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n != 0);
if (n + vec.len() > vec.capacity()) { if (n + vec.len() > vec.capacity()) {
// we can't shift more than the capacity of the vector.
return false; return false;
} else if (!vec.is_empty()) { }
if (vec.is_empty()) {
// nothing to do
return true;
}
// move limbs // move limbs
limb *dst = vec.data + n; limb *dst = vec.data + n;
limb const *src = vec.data; limb const *src = vec.data;
@ -545,17 +547,14 @@ struct bigint : pow5_tables<> {
limb *first = vec.data; limb *first = vec.data;
limb *last = first + n; limb *last = first + n;
::std::fill(first, last, 0); ::std::fill(first, last, 0);
vec.set_len(uint8_t(n + vec.len())); vec.set_len(limb_t(n + vec.len()));
return true; return true;
} else {
return true;
}
} }
// move the limbs left by `n` bits. // move the limbs left by `n` bits.
FASTFLOAT_CONSTEXPR20 bool shl(uint16_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl(bigint_bits_t n) noexcept {
uint16_t const rem = n % limb_bits; bigint_bits_t const rem = n % limb_bits;
uint16_t const div = n / limb_bits; bigint_bits_t const div = n / limb_bits;
if (rem != 0) { if (rem != 0) {
FASTFLOAT_TRY(shl_bits(rem)); FASTFLOAT_TRY(shl_bits(rem));
} }
@ -566,10 +565,11 @@ struct bigint : pow5_tables<> {
} }
// get the number of leading zeros in the bigint. // get the number of leading zeros in the bigint.
FASTFLOAT_CONSTEXPR20 uint8_t ctlz() const noexcept { FASTFLOAT_CONSTEXPR20 limb_t ctlz() const noexcept {
if (vec.is_empty()) { if (vec.is_empty()) {
// empty vector, no bits, no zeros.
return 0; return 0;
} else { }
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
return leading_zeroes(vec.rindex(0)); return leading_zeroes(vec.rindex(0));
#else #else
@ -578,11 +578,10 @@ struct bigint : pow5_tables<> {
return leading_zeroes(r0 << 32); return leading_zeroes(r0 << 32);
#endif #endif
} }
}
// get the number of bits in the bigint. // get the number of bits in the bigint.
FASTFLOAT_CONSTEXPR20 uint16_t bit_length() const noexcept { FASTFLOAT_CONSTEXPR20 bigint_bits_t bit_length() const noexcept {
uint8_t lz = ctlz(); limb_t lz = ctlz();
return limb_bits * vec.len() - lz; return limb_bits * vec.len() - lz;
} }
@ -591,22 +590,22 @@ struct bigint : pow5_tables<> {
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); } FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
// multiply as if by 2 raised to a power. // multiply as if by 2 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow2(int16_t exp) noexcept { return shl(exp); } FASTFLOAT_CONSTEXPR20 bool pow2(am_pow_t exp) noexcept { return shl(exp); }
// multiply as if by 5 raised to a power. // multiply as if by 5 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow5(int16_t exp) noexcept { FASTFLOAT_CONSTEXPR20 bool pow5(am_pow_t exp) noexcept {
// multiply by a power of 5 // multiply by a power of 5
uint8_t const large_length = sizeof(large_power_of_5) / sizeof(limb); limb_t const large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span const large = limb_span(large_power_of_5, large_length); limb_span const large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) { while (exp >= large_step) {
FASTFLOAT_TRY(large_mul(vec, large)); FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step; exp -= large_step;
} }
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
uint8_t const small_step = 27; limb_t const small_step = 27;
limb const max_native = 7450580596923828125UL; limb const max_native = 7450580596923828125UL;
#else #else
uint8_t const small_step = 13; limb_t const small_step = 13;
limb const max_native = 1220703125U; limb const max_native = 1220703125U;
#endif #endif
while (exp >= small_step) { while (exp >= small_step) {
@ -625,7 +624,7 @@ struct bigint : pow5_tables<> {
} }
// multiply as if by 10 raised to a power. // multiply as if by 10 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow10(int16_t exp) noexcept { FASTFLOAT_CONSTEXPR20 bool pow10(am_pow_t exp) noexcept {
FASTFLOAT_TRY(pow5(exp)); FASTFLOAT_TRY(pow5(exp));
return pow2(exp); return pow2(exp);
} }

View File

@ -76,7 +76,7 @@ compute_error_scaled(int64_t q, uint64_t w, int32_t lz) noexcept {
adjusted_mantissa answer; adjusted_mantissa answer;
answer.mantissa = w << hilz; answer.mantissa = w << hilz;
int32_t bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); int32_t bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
answer.power2 = int16_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + answer.power2 = am_pow_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 +
invalid_am_bias); invalid_am_bias);
return answer; return answer;
} }
@ -143,9 +143,9 @@ compute_float(int64_t q, uint64_t w) noexcept {
answer.mantissa = product.high >> shift; answer.mantissa = product.high >> shift;
answer.power2 = int16_t(detail::power(int32_t(q)) + upperbit - lz - answer.power2 = am_pow_t(detail::power(int32_t(q)) + upperbit - lz -
binary::minimum_exponent()); binary::minimum_exponent());
if (answer.power2 <= 0) { // we have a subnormal? if (answer.power2 <= 0) { // we have a subnormal or very small value.
// Here have that answer.power2 <= 0 so -answer.power2 >= 0 // Here have that answer.power2 <= 0 so -answer.power2 >= 0
if (-answer.power2 + 1 >= if (-answer.power2 + 1 >=
64) { // if we have more than 64 bits below the minimum exponent, you 64) { // if we have more than 64 bits below the minimum exponent, you
@ -155,6 +155,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
// result should be zero // result should be zero
return answer; return answer;
} }
// We have a subnormal number. We need to shift the mantissa to the right
// next line is safe because -answer.power2 + 1 < 64 // next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1; answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because // Thankfully, we can't have both "round-to-even" and subnormals because

View File

@ -41,8 +41,8 @@ constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int16_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int16_t
scientific_exponent(parsed_number_string_t<UC> const &num) noexcept { scientific_exponent(parsed_number_string_t<UC> const &num) noexcept {
uint64_t mantissa = num.mantissa; am_mant_t mantissa = num.mantissa;
int16_t exponent = num.exponent; am_pow_t exponent = num.exponent;
while (mantissa >= 10000) { while (mantissa >= 10000) {
mantissa /= 10000; mantissa /= 10000;
exponent += 4; exponent += 4;
@ -68,11 +68,15 @@ to_extended(T const &value) noexcept {
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am; adjusted_mantissa am;
int16_t bias = binary_format<T>::mantissa_explicit_bits() - am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent(); binary_format<T>::minimum_exponent();
equiv_uint bits; equiv_uint bits;
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
bits = std::bit_cast<equiv_uint>(value); bits =
#if FASTFLOAT_HAS_BIT_CAST == 1
std::
#endif
bit_cast<equiv_uint>(value);
#else #else
::memcpy(&bits, &value, sizeof(T)); ::memcpy(&bits, &value, sizeof(T));
#endif #endif
@ -82,7 +86,7 @@ to_extended(T const &value) noexcept {
am.mantissa = bits & mantissa_mask; am.mantissa = bits & mantissa_mask;
} else { } else {
// normal // normal
am.power2 = int16_t((bits & exponent_mask) >> am.power2 = am_pow_t((bits & exponent_mask) >>
binary_format<T>::mantissa_explicit_bits()); binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias; am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
@ -108,14 +112,14 @@ to_extended_halfway(T const &value) noexcept {
template <typename T, typename callback> template <typename T, typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am, fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
callback cb) noexcept { callback cb) noexcept {
int16_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; am_pow_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) { if (-am.power2 >= mantissa_shift) {
// have a denormal float // have a denormal float
int16_t shift = -am.power2 + 1; am_pow_t shift = -am.power2 + 1;
cb(am, std::min<int16_t>(shift, 64)); cb(am, std::min<int16_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit. // check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < am.power2 = (am.mantissa <
(uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) (am_mant_t(1) << binary_format<T>::mantissa_explicit_bits()))
? 0 ? 0
: 1; : 1;
return; return;
@ -126,13 +130,13 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
// check for carry // check for carry
if (am.mantissa >= if (am.mantissa >=
(uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) { (am_mant_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); am.mantissa = (am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
++am.power2; ++am.power2;
} }
// check for infinite: we could have carried to an infinite power // check for infinite: we could have carried to an infinite power
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); am.mantissa &= ~(am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
if (am.power2 >= binary_format<T>::infinite_power()) { if (am.power2 >= binary_format<T>::infinite_power()) {
am.power2 = binary_format<T>::infinite_power(); am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0; am.mantissa = 0;
@ -141,11 +145,11 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
template <typename callback> template <typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
round_nearest_tie_even(adjusted_mantissa &am, int16_t shift, round_nearest_tie_even(adjusted_mantissa &am, am_pow_t shift,
callback cb) noexcept { callback cb) noexcept {
uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1; am_mant_t const mask = (shift == 64) ? UINT64_MAX : (am_mant_t(1) << shift) - 1;
uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1); am_mant_t const halfway = (shift == 0) ? 0 : am_mant_t(1) << (shift - 1);
uint64_t truncated_bits = am.mantissa & mask; am_mant_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway; bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway; bool is_halfway = truncated_bits == halfway;
@ -158,11 +162,11 @@ round_nearest_tie_even(adjusted_mantissa &am, int16_t shift,
am.power2 += shift; am.power2 += shift;
bool is_odd = (am.mantissa & 1) == 1; bool is_odd = (am.mantissa & 1) == 1;
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); am.mantissa += am_mant_t(cb(is_odd, is_halfway, is_above));
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
round_down(adjusted_mantissa &am, int16_t shift) noexcept { round_down(adjusted_mantissa &am, am_pow_t shift) noexcept {
if (shift == 64) { if (shift == 64) {
am.mantissa = 0; am.mantissa = 0;
} else { } else {
@ -223,8 +227,8 @@ is_truncated(span<UC const> s) noexcept {
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
parse_eight_digits(UC const *&p, limb &value, uint16_t &counter, parse_eight_digits(UC const *&p, limb &value, am_digits &counter,
uint16_t &count) noexcept { am_digits &count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p); value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8; p += 8;
counter += 8; counter += 8;
@ -233,8 +237,8 @@ parse_eight_digits(UC const *&p, limb &value, uint16_t &counter,
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
parse_one_digit(UC const *&p, limb &value, uint16_t &counter, parse_one_digit(UC const *&p, limb &value, am_digits &counter,
uint16_t &count) noexcept { am_digits &count) noexcept {
value = value * 10 + limb(*p - UC('0')); value = value * 10 + limb(*p - UC('0'));
++p; ++p;
++counter; ++counter;
@ -248,7 +252,7 @@ add_native(bigint &big, limb power, limb value) noexcept {
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
round_up_bigint(bigint &big, uint16_t &count) noexcept { round_up_bigint(bigint &big, am_digits &count) noexcept {
// need to round-up the digits, but need to avoid rounding // need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point. // ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1); add_native(big, 10, 1);
@ -257,19 +261,19 @@ round_up_bigint(bigint &big, uint16_t &count) noexcept {
// parse the significant digits into a big integer // parse the significant digits into a big integer
template <typename T, typename UC> template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 uint16_t inline FASTFLOAT_CONSTEXPR20 am_digits
parse_mantissa(bigint &result, const parsed_number_string_t<UC> &num) noexcept { parse_mantissa(bigint &result, const parsed_number_string_t<UC> &num) noexcept {
// try to minimize the number of big integer and scalar multiplication. // try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest // therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step. // scalar value (9 or 19 digits) for each step.
uint16_t const max_digits = binary_format<T>::max_digits(); am_digits const max_digits = binary_format<T>::max_digits();
uint16_t counter = 0; am_digits counter = 0;
uint16_t digits = 0; am_digits digits = 0;
limb value = 0; limb value = 0;
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
uint16_t const step = 19; am_digits const step = 19;
#else #else
uint16_t const step = 9; am_digits const step = 9;
#endif #endif
// process all integer digits. // process all integer digits.
@ -343,15 +347,15 @@ parse_mantissa(bigint &result, const parsed_number_string_t<UC> &num) noexcept {
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp( inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp(
bigint &bigmant, adjusted_mantissa am, int16_t const exponent) noexcept { bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(exponent)); FASTFLOAT_ASSERT(bigmant.pow10(exponent));
bool truncated; bool truncated;
am.mantissa = bigmant.hi64(truncated); am.mantissa = bigmant.hi64(truncated);
int16_t bias = binary_format<T>::mantissa_explicit_bits() - am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent(); binary_format<T>::minimum_exponent();
am.power2 = bigmant.bit_length() - 64 + bias; am.power2 = bigmant.bit_length() - 64 + bias;
round<T>(am, [truncated](adjusted_mantissa &a, int16_t shift) { round<T>(am, [truncated](adjusted_mantissa &a, am_pow_t shift) {
round_nearest_tie_even( round_nearest_tie_even(
a, shift, a, shift,
[truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
@ -370,9 +374,9 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp(
// are of the same magnitude. // are of the same magnitude.
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp( inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
bigint &bigmant, adjusted_mantissa am, int16_t const exponent) noexcept { bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
bigint &real_digits = bigmant; bigint &real_digits = bigmant;
int16_t const &real_exp = exponent; am_pow_t const &real_exp = exponent;
T b; T b;
{ {
@ -382,7 +386,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
// gcc7 bug: use a lambda to remove the noexcept qualifier bug with // gcc7 bug: use a lambda to remove the noexcept qualifier bug with
// -Wnoexcept-type. // -Wnoexcept-type.
round<T>(am_b, round<T>(am_b,
[](adjusted_mantissa &a, int16_t shift) { round_down(a, shift); }); [](adjusted_mantissa &a, am_pow_t shift) { round_down(a, shift); });
to_float( to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false, false,
@ -391,11 +395,11 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
} }
adjusted_mantissa theor = to_extended_halfway(b); adjusted_mantissa theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa); bigint theor_digits(theor.mantissa);
int16_t theor_exp = theor.power2; am_pow_t theor_exp = theor.power2;
// scale real digits and theor digits to be same power. // scale real digits and theor digits to be same power.
int16_t pow2_exp = theor_exp - real_exp; am_pow_t pow2_exp = theor_exp - real_exp;
uint16_t pow5_exp = -real_exp; am_pow_t pow5_exp = -real_exp;
if (pow5_exp != 0) { if (pow5_exp != 0) {
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
} }
@ -407,7 +411,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
// compare digits, and use it to director rounding // compare digits, and use it to director rounding
int ord = real_digits.compare(theor_digits); int ord = real_digits.compare(theor_digits);
round<T>(am, [ord](adjusted_mantissa &a, int16_t shift) { round<T>(am, [ord](adjusted_mantissa &a, am_pow_t shift) {
round_nearest_tie_even( round_nearest_tie_even(
a, shift, [ord](bool is_odd, bool _, bool __) -> bool { a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
(void)_; // not needed, since we've done our comparison (void)_; // not needed, since we've done our comparison
@ -445,11 +449,11 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa digit_comp(
am.power2 -= invalid_am_bias; am.power2 -= invalid_am_bias;
bigint bigmant; bigint bigmant;
int16_t const sci_exp = scientific_exponent(num); am_pow_t const sci_exp = scientific_exponent(num);
uint16_t const digits = parse_mantissa<T, UC>(bigmant, num); am_digits const digits = parse_mantissa<T, UC>(bigmant, num);
// can't underflow, since digits is at most max_digits. // can't underflow, since digits is at most max_digits.
int16_t const exponent = sci_exp + 1 - digits; am_pow_t const exponent = sci_exp + 1 - digits;
if (exponent >= 0) { if (exponent >= 0) {
return positive_digit_comp<T>(bigmant, am, exponent); return positive_digit_comp<T>(bigmant, am, exponent);
} else { } else {

View File

@ -33,6 +33,12 @@
namespace fast_float { namespace fast_float {
// The number of digits in the mantissa.
typedef uint16_t am_digits;
// The number of bits in the limb.
typedef uint8_t limb_t;
typedef uint8_t chars_format_t; typedef uint8_t chars_format_t;
enum class chars_format : chars_format_t; enum class chars_format : chars_format_t;
@ -280,12 +286,13 @@ struct is_supported_char_type
> { > {
}; };
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// Compares two ASCII strings in a case insensitive manner. // Compares two ASCII strings in a case insensitive manner.
template <typename UC> template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase, fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
size_t length) noexcept { uint8_t const length) noexcept {
for (size_t i = 0; i < length; ++i) { for (uint8_t i = 0; i != length; ++i) {
UC const actual = actual_mixedcase[i]; UC const actual = actual_mixedcase[i];
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) { if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
return false; return false;
@ -293,6 +300,7 @@ fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
} }
return true; return true;
} }
#endif
#ifndef FLT_EVAL_METHOD #ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat." #error "FLT_EVAL_METHOD should be defined, please include cfloat."
@ -301,16 +309,16 @@ fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
// a pointer and a length to a contiguous block of memory // a pointer and a length to a contiguous block of memory
template <typename T> struct span { template <typename T> struct span {
T const *ptr; T const *ptr;
uint16_t length; am_digits length;
constexpr span(T const *_ptr, uint16_t _length) noexcept constexpr span(T const *_ptr, am_digits _length) noexcept
: ptr(_ptr), length(_length) {} : ptr(_ptr), length(_length) {}
constexpr span() noexcept : ptr(nullptr), length(0) {} constexpr span() noexcept : ptr(nullptr), length(0) {}
constexpr uint16_t len() const noexcept { return length; } constexpr am_digits len() const noexcept { return length; }
FASTFLOAT_CONSTEXPR14 const T &operator[](uint16_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const T &operator[](am_digits index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index]; return ptr[index];
} }
@ -327,7 +335,7 @@ struct value128 {
}; };
/* Helper C++14 constexpr generic implementation of leading_zeroes */ /* Helper C++14 constexpr generic implementation of leading_zeroes */
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint8_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 limb_t
leading_zeroes_generic(uint64_t input_num, uint64_t last_bit = 0) noexcept { leading_zeroes_generic(uint64_t input_num, uint64_t last_bit = 0) noexcept {
if (input_num & uint64_t(0xffffffff00000000)) { if (input_num & uint64_t(0xffffffff00000000)) {
input_num >>= 32; input_num >>= 32;
@ -352,11 +360,11 @@ leading_zeroes_generic(uint64_t input_num, uint64_t last_bit = 0) noexcept {
if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */ if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */
last_bit |= 1; last_bit |= 1;
} }
return 63 - (uint8_t)last_bit; return 63 - (limb_t)last_bit;
} }
/* result might be undefined when input_num is zero */ /* result might be undefined when input_num is zero */
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint8_t fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb_t
leading_zeroes(uint64_t input_num) noexcept { leading_zeroes(uint64_t input_num) noexcept {
assert(input_num > 0); assert(input_num > 0);
FASTFLOAT_ASSUME(input_num > 0); FASTFLOAT_ASSUME(input_num > 0);
@ -369,12 +377,12 @@ leading_zeroes(uint64_t input_num) noexcept {
// Search the mask data from most significant bit (MSB) // Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1). // to least significant bit (LSB) for a set bit (1).
_BitScanReverse64(&leading_zero, input_num); _BitScanReverse64(&leading_zero, input_num);
return (uint8_t)(63 - leading_zero); return (limb_t)(63 - leading_zero);
#else #else
return (uint8_t)leading_zeroes_generic(input_num); return (limb_t)leading_zeroes_generic(input_num);
#endif #endif
#else #else
return (uint8_t)__builtin_clzll(input_num); return (limb_t)__builtin_clzll(input_num);
#endif #endif
} }
@ -436,9 +444,21 @@ full_multiplication(uint64_t a, uint64_t b) noexcept {
return answer; return answer;
} }
// Value of the mantissa.
typedef uint64_t am_mant_t;
// Size of bits in the mantissa.
typedef uint8_t am_bits_t;
// Power bias is signed for handling a denormal float
// or an invalid mantissa.
typedef int16_t am_pow_t;
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
constexpr static am_pow_t invalid_am_bias = -0x8000;
struct adjusted_mantissa { struct adjusted_mantissa {
uint64_t mantissa; am_mant_t mantissa;
int16_t power2; // a negative value indicates an invalid result am_pow_t power2;
adjusted_mantissa() noexcept = default; adjusted_mantissa() noexcept = default;
constexpr bool operator==(adjusted_mantissa const &o) const noexcept { constexpr bool operator==(adjusted_mantissa const &o) const noexcept {
@ -450,35 +470,30 @@ struct adjusted_mantissa {
} }
}; };
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
constexpr static int32_t invalid_am_bias = -0x8000;
// used for binary_format_lookup_tables<T>::max_mantissa // used for binary_format_lookup_tables<T>::max_mantissa
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; constexpr am_mant_t constant_55555 = 5 * 5 * 5 * 5 * 5;
template <typename T, typename U = void> struct binary_format_lookup_tables; template <typename T, typename U = void> struct binary_format_lookup_tables;
template <typename T> struct binary_format : binary_format_lookup_tables<T> { template <typename T> struct binary_format : binary_format_lookup_tables<T> {
using equiv_uint = equiv_uint_t<T>; using equiv_uint = equiv_uint_t<T>;
// TODO add type for bit shift operations and use it.
// TODO add type for exponent operations and use it.
static constexpr uint8_t mantissa_explicit_bits(); static constexpr am_bits_t mantissa_explicit_bits();
static constexpr int16_t minimum_exponent(); static constexpr am_pow_t minimum_exponent();
static constexpr int16_t infinite_power(); static constexpr am_pow_t infinite_power();
static constexpr uint8_t sign_index(); static constexpr am_bits_t sign_index();
static constexpr int8_t static constexpr am_pow_t
min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
static constexpr int8_t max_exponent_fast_path(); static constexpr am_pow_t max_exponent_fast_path();
static constexpr int16_t max_exponent_round_to_even(); static constexpr am_pow_t max_exponent_round_to_even();
static constexpr int16_t min_exponent_round_to_even(); static constexpr am_pow_t min_exponent_round_to_even();
static constexpr equiv_uint max_mantissa_fast_path(int64_t power); static constexpr equiv_uint max_mantissa_fast_path(int64_t power);
static constexpr equiv_uint static constexpr equiv_uint
max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
static constexpr int16_t largest_power_of_ten(); static constexpr am_pow_t largest_power_of_ten();
static constexpr int16_t smallest_power_of_ten(); static constexpr am_pow_t smallest_power_of_ten();
static constexpr T exact_power_of_ten(int64_t power); static constexpr T exact_power_of_ten(int64_t power);
static constexpr uint16_t max_digits(); static constexpr am_digits max_digits();
static constexpr equiv_uint exponent_mask(); static constexpr equiv_uint exponent_mask();
static constexpr equiv_uint mantissa_mask(); static constexpr equiv_uint mantissa_mask();
static constexpr equiv_uint hidden_bit_mask(); static constexpr equiv_uint hidden_bit_mask();
@ -568,7 +583,7 @@ constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[];
#endif #endif
template <> template <>
inline constexpr int8_t binary_format<double>::min_exponent_fast_path() { inline constexpr am_pow_t binary_format<double>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0; return 0;
#else #else
@ -577,7 +592,7 @@ inline constexpr int8_t binary_format<double>::min_exponent_fast_path() {
} }
template <> template <>
inline constexpr int8_t binary_format<float>::min_exponent_fast_path() { inline constexpr am_pow_t binary_format<float>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0; return 0;
#else #else
@ -586,81 +601,76 @@ inline constexpr int8_t binary_format<float>::min_exponent_fast_path() {
} }
template <> template <>
inline constexpr uint8_t binary_format<double>::mantissa_explicit_bits() { inline constexpr am_bits_t binary_format<double>::mantissa_explicit_bits() {
return 52; return 52;
} }
template <> template <>
inline constexpr uint8_t binary_format<float>::mantissa_explicit_bits() { inline constexpr am_bits_t binary_format<float>::mantissa_explicit_bits() {
return 23; return 23;
} }
template <> template <>
inline constexpr int16_t binary_format<double>::max_exponent_round_to_even() { inline constexpr am_pow_t binary_format<double>::max_exponent_round_to_even() {
return 23; return 23;
} }
template <> template <>
inline constexpr int16_t binary_format<float>::max_exponent_round_to_even() { inline constexpr am_pow_t binary_format<float>::max_exponent_round_to_even() {
return 10; return 10;
} }
template <> template <>
inline constexpr int16_t binary_format<double>::min_exponent_round_to_even() { inline constexpr am_pow_t binary_format<double>::min_exponent_round_to_even() {
return -4; return -4;
} }
template <> template <>
inline constexpr int16_t binary_format<float>::min_exponent_round_to_even() { inline constexpr am_pow_t binary_format<float>::min_exponent_round_to_even() {
return -17; return -17;
} }
template <> inline constexpr int16_t binary_format<double>::minimum_exponent() { template <> inline constexpr am_pow_t binary_format<double>::minimum_exponent() {
return -1023; return -1023;
} }
template <> inline constexpr int16_t binary_format<float>::minimum_exponent() { template <> inline constexpr am_pow_t binary_format<float>::minimum_exponent() {
return -127; return -127;
} }
template <> inline constexpr int16_t binary_format<double>::infinite_power() { template <> inline constexpr am_pow_t binary_format<double>::infinite_power() {
return 0x7FF; return 0x7FF;
} }
template <> inline constexpr int16_t binary_format<float>::infinite_power() { template <> inline constexpr am_pow_t binary_format<float>::infinite_power() {
return 0xFF; return 0xFF;
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <> inline constexpr uint8_t binary_format<double>::sign_index() { template <> inline constexpr am_bits_t binary_format<double>::sign_index() {
return 63; return 63;
} }
template <> inline constexpr uint8_t binary_format<float>::sign_index() { template <> inline constexpr am_bits_t binary_format<float>::sign_index() {
return 31; return 31;
} }
#endif #endif
template <> template <>
inline constexpr int8_t binary_format<double>::max_exponent_fast_path() { inline constexpr am_pow_t binary_format<double>::max_exponent_fast_path() {
return 22; return 22;
} }
template <> template <>
inline constexpr int8_t binary_format<float>::max_exponent_fast_path() { inline constexpr am_pow_t binary_format<float>::max_exponent_fast_path() {
return 10; return 10;
} }
template <> template <typename T>
inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() { inline constexpr binary_format<T>::equiv_uint binary_format<T>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits(); return binary_format<T>::equiv_uint(2) << mantissa_explicit_bits();
}
template <>
inline constexpr uint32_t binary_format<float>::max_mantissa_fast_path() {
return uint32_t(2) << mantissa_explicit_bits();
} }
// credit: Jakub Jelínek // credit: Jakub Jelínek
@ -728,12 +738,6 @@ binary_format<std::float16_t>::mantissa_explicit_bits() {
return 10; return 10;
} }
template <>
inline constexpr int8_t
binary_format<std::float16_t>::max_mantissa_fast_path() {
return uint16_t(2) << mantissa_explicit_bits();
}
template <> template <>
inline constexpr uint64_t inline constexpr uint64_t
binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) { binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {
@ -763,37 +767,37 @@ binary_format<std::float16_t>::min_exponent_round_to_even() {
} }
template <> template <>
inline constexpr int16_t binary_format<std::float16_t>::minimum_exponent() { inline constexpr am_exp_t binary_format<std::float16_t>::minimum_exponent() {
return -15; return -15;
} }
template <> template <>
inline constexpr int16_t binary_format<std::float16_t>::infinite_power() { inline constexpr am_exp_t binary_format<std::float16_t>::infinite_power() {
return 0x1F; return 0x1F;
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <> template <>
inline constexpr uint8_t binary_format<std::float16_t>::sign_index() { inline constexpr am_bits_t binary_format<std::float16_t>::sign_index() {
return 15; return 15;
} }
#endif #endif
template <> template <>
inline constexpr int16_t binary_format<std::float16_t>::largest_power_of_ten() { inline constexpr am_exp_t binary_format<std::float16_t>::largest_power_of_ten() {
return 4; return 4;
} }
template <> template <>
inline constexpr int16_t inline constexpr am_exp_t
binary_format<std::float16_t>::smallest_power_of_ten() { binary_format<std::float16_t>::smallest_power_of_ten() {
return -27; return -27;
} }
template <> template <>
inline constexpr uint8_t binary_format<std::float16_t>::max_digits() { inline constexpr am_digits binary_format<std::float16_t>::max_digits() {
return 22; return 22;
} }
#endif // __STDCPP_FLOAT16_T__ #endif // __STDCPP_FLOAT16_T__
@ -860,13 +864,6 @@ binary_format<std::bfloat16_t>::mantissa_explicit_bits() {
return 7; return 7;
} }
template <>
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::max_mantissa_fast_path() {
return binary_format<std::bfloat16_t>::equiv_uint(2)
<< mantissa_explicit_bits();
}
template <> template <>
inline constexpr uint64_t inline constexpr uint64_t
binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) { binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {
@ -884,24 +881,24 @@ binary_format<std::bfloat16_t>::min_exponent_fast_path() {
} }
template <> template <>
inline constexpr int16_t inline constexpr am_exp_t
binary_format<std::bfloat16_t>::max_exponent_round_to_even() { binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
return 3; return 3;
} }
template <> template <>
inline constexpr int16_t inline constexpr am_exp_t
binary_format<std::bfloat16_t>::min_exponent_round_to_even() { binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
return -24; return -24;
} }
template <> template <>
inline constexpr int16_t binary_format<std::bfloat16_t>::minimum_exponent() { inline constexpr am_exp_t binary_format<std::bfloat16_t>::minimum_exponent() {
return -127; return -127;
} }
template <> template <>
inline constexpr int16_t binary_format<std::bfloat16_t>::infinite_power() { inline constexpr am_exp_t binary_format<std::bfloat16_t>::infinite_power() {
return 0xFF; return 0xFF;
} }
@ -915,13 +912,13 @@ inline constexpr uint8_t binary_format<std::bfloat16_t>::sign_index() {
#endif #endif
template <> template <>
inline constexpr int16_t inline constexpr am_exp_t
binary_format<std::bfloat16_t>::largest_power_of_ten() { binary_format<std::bfloat16_t>::largest_power_of_ten() {
return 38; return 38;
} }
template <> template <>
inline constexpr int16_t inline constexpr am_exp_t
binary_format<std::bfloat16_t>::smallest_power_of_ten() { binary_format<std::bfloat16_t>::smallest_power_of_ten() {
return -60; return -60;
} }
@ -972,30 +969,30 @@ inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
} }
template <> template <>
inline constexpr int16_t binary_format<double>::largest_power_of_ten() { inline constexpr am_pow_t binary_format<double>::largest_power_of_ten() {
return 308; return 308;
} }
template <> template <>
inline constexpr int16_t binary_format<float>::largest_power_of_ten() { inline constexpr am_pow_t binary_format<float>::largest_power_of_ten() {
return 38; return 38;
} }
template <> template <>
inline constexpr int16_t binary_format<double>::smallest_power_of_ten() { inline constexpr am_pow_t binary_format<double>::smallest_power_of_ten() {
return -342; return -342;
} }
template <> template <>
inline constexpr int16_t binary_format<float>::smallest_power_of_ten() { inline constexpr am_pow_t binary_format<float>::smallest_power_of_ten() {
return -64; return -64;
} }
template <> inline constexpr uint16_t binary_format<double>::max_digits() { template <> inline constexpr am_digits binary_format<double>::max_digits() {
return 769; return 769;
} }
template <> inline constexpr uint16_t binary_format<float>::max_digits() { template <> inline constexpr am_digits binary_format<float>::max_digits() {
return 114; return 114;
} }