diff --git a/include/fmt/format.h b/include/fmt/format.h index 53884a13..e665d612 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1028,6 +1028,14 @@ using format_specs = basic_format_specs; namespace internal { +struct gen_digits_params { + int num_digits; + sign_t sign : 3; + bool fixed; + bool upper; + bool trailing_zeros; +}; + // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template It write_exponent(int exp, It it) { FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range"); @@ -1047,79 +1055,9 @@ template It write_exponent(int exp, It it) { return it; } -struct gen_digits_params { - int num_digits; - sign_t sign : 3; - bool fixed; - bool upper; - bool trailing_zeros; -}; - -// The number is given as v = digits * pow(10, exp). -template -It grisu_prettify(const char* digits, int size, int exp, It it, - gen_digits_params params, Char decimal_point) { - // pow(10, full_exp - 1) <= v <= pow(10, full_exp). - int full_exp = size + exp; - if (!params.fixed) { - // Insert a decimal point after the first digit and add an exponent. - *it++ = static_cast(*digits); - if (size > 1) *it++ = decimal_point; - exp += size - 1; - it = copy_str(digits + 1, digits + size, it); - if (size < params.num_digits) - it = std::fill_n(it, params.num_digits - size, static_cast('0')); - *it++ = static_cast(params.upper ? 'E' : 'e'); - return write_exponent(exp, it); - } - if (size <= full_exp) { - // 1234e7 -> 12340000000[.0+] - it = copy_str(digits, digits + size, it); - it = std::fill_n(it, full_exp - size, static_cast('0')); - int num_zeros = (std::max)(params.num_digits - full_exp, 1); - if (params.trailing_zeros) { - *it++ = decimal_point; -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (num_zeros > 1000) - throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); -#endif - it = std::fill_n(it, num_zeros, static_cast('0')); - } - } else if (full_exp > 0) { - // 1234e-2 -> 12.34[0+] - it = copy_str(digits, digits + full_exp, it); - if (!params.trailing_zeros) { - // Remove trailing zeros. - while (size > full_exp && digits[size - 1] == '0') --size; - if (size != full_exp) *it++ = decimal_point; - return copy_str(digits + full_exp, digits + size, it); - } - *it++ = decimal_point; - it = copy_str(digits + full_exp, digits + size, it); - if (params.num_digits > size) { - // Add trailing zeros. - int num_zeros = params.num_digits - size; - it = std::fill_n(it, num_zeros, static_cast('0')); - } - } else { - // 1234e-6 -> 0.001234 - *it++ = static_cast('0'); - int num_zeros = -full_exp; - if (params.num_digits >= 0 && params.num_digits < num_zeros) - num_zeros = params.num_digits; - if (!params.trailing_zeros) - while (size > 0 && digits[size - 1] == '0') --size; - if (num_zeros != 0 || size != 0) { - *it++ = decimal_point; - it = std::fill_n(it, num_zeros, static_cast('0')); - it = copy_str(digits, digits + size, it); - } - } - return it; -} - template class grisu_writer { private: + // The number is given as v = digits_ * pow(10, exp_). const char* digits_; int num_digits_; int exp_; @@ -1127,6 +1065,70 @@ template class grisu_writer { gen_digits_params params_; Char decimal_point_; + template It grisu_prettify(It it) const { + // pow(10, full_exp - 1) <= v <= pow(10, full_exp). + int full_exp = num_digits_ + exp_; + if (!params_.fixed) { + // Insert a decimal point after the first digit and add an exponent. + *it++ = static_cast(*digits_); + if (num_digits_ > 1) *it++ = decimal_point_; + it = copy_str(digits_ + 1, digits_ + num_digits_, it); + if (num_digits_ < params_.num_digits) { + it = std::fill_n(it, params_.num_digits - num_digits_, + static_cast('0')); + } + *it++ = static_cast(params_.upper ? 'E' : 'e'); + return write_exponent(full_exp - 1, it); + } + if (num_digits_ <= full_exp) { + // 1234e7 -> 12340000000[.0+] + it = copy_str(digits_, digits_ + num_digits_, it); + it = std::fill_n(it, full_exp - num_digits_, static_cast('0')); + int num_zeros = (std::max)(params_.num_digits - full_exp, 1); + if (params_.trailing_zeros) { + *it++ = decimal_point_; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (num_zeros > 1000) + throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); +#endif + it = std::fill_n(it, num_zeros, static_cast('0')); + } + } else if (full_exp > 0) { + // 1234e-2 -> 12.34[0+] + it = copy_str(digits_, digits_ + full_exp, it); + if (!params_.trailing_zeros) { + // Remove trailing zeros. + int num_digits = num_digits_; + while (num_digits > full_exp && digits_[num_digits - 1] == '0') + --num_digits; + if (num_digits != full_exp) *it++ = decimal_point_; + return copy_str(digits_ + full_exp, digits_ + num_digits, it); + } + *it++ = decimal_point_; + it = copy_str(digits_ + full_exp, digits_ + num_digits_, it); + if (params_.num_digits > num_digits_) { + // Add trailing zeros. + int num_zeros = params_.num_digits - num_digits_; + it = std::fill_n(it, num_zeros, static_cast('0')); + } + } else { + // 1234e-6 -> 0.001234 + *it++ = static_cast('0'); + int num_zeros = -full_exp; + if (params_.num_digits >= 0 && params_.num_digits < num_zeros) + num_zeros = params_.num_digits; + int num_digits = num_digits_; + if (!params_.trailing_zeros) + while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits; + if (num_zeros != 0 || num_digits != 0) { + *it++ = decimal_point_; + it = std::fill_n(it, num_zeros, static_cast('0')); + it = copy_str(digits_, digits_ + num_digits, it); + } + } + return it; + } + public: grisu_writer(const char* digits, int num_digits, int exp, gen_digits_params params, Char decimal_point) @@ -1138,18 +1140,16 @@ template class grisu_writer { int full_exp = num_digits + exp - 1; int precision = params.num_digits > 0 ? params.num_digits : 16; params_.fixed |= full_exp >= -4 && full_exp < precision; - auto it = grisu_prettify(digits, num_digits, exp, counting_iterator(), - params_, '\0'); - size_ = it.count(); + size_ = grisu_prettify(counting_iterator()).count(); + size_ += params_.sign ? 1 : 0; } - size_t size() const { return size_ + (params_.sign ? 1 : 0); } + size_t size() const { return size_; } size_t width() const { return size(); } template void operator()(It&& it) { if (params_.sign) *it++ = static_cast(data::signs[params_.sign]); - it = - grisu_prettify(digits_, num_digits_, exp_, it, params_, decimal_point_); + it = grisu_prettify(it); } };