use-double-conversion.patch (7717B)
1 diff --git a/include/fmt/format.h b/include/fmt/format.h 2 --- a/include/fmt/format.h 3 +++ b/include/fmt/format.h 4 @@ -28,16 +28,18 @@ 5 of this Software are embedded into a machine-executable object form of such 6 source code, you may redistribute such embedded portions in such object form 7 without including the above copyright and permission notices. 8 */ 9 10 #ifndef FMT_FORMAT_H_ 11 #define FMT_FORMAT_H_ 12 13 +#include "double-conversion/double-to-string.h" 14 + 15 #ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES 16 # define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES 17 # define FMT_REMOVE_TRANSITIVE_INCLUDES 18 #endif 19 20 #include "base.h" 21 22 #ifndef FMT_MODULE 23 @@ -3141,16 +3143,23 @@ constexpr auto fractional_part_rounding_ 24 return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7" 25 U"\x800001ae\x8000002b"[index]; 26 } 27 28 template <typename Float> 29 FMT_CONSTEXPR20 auto format_float(Float value, int precision, 30 const format_specs& specs, bool binary32, 31 buffer<char>& buf) -> int { 32 + // GCC and old clang seem to hit this even though this function isn't called. 33 + #ifdef __clang__ 34 + #if __clang_major__ >= 17 35 + static_assert(false, 36 + "This method is not to be used in Gecko, use format_float_gecko"); 37 + #endif 38 + #endif 39 // float is passed as double to reduce the number of instantiations. 40 static_assert(!std::is_same<Float, float>::value, ""); 41 auto converted_value = convert_float(value); 42 43 const bool fixed = specs.type() == presentation_type::fixed; 44 if (value == 0) { 45 if (precision <= 0 || !fixed) { 46 buf.push_back('0'); 47 @@ -3436,68 +3445,109 @@ FMT_CONSTEXPR20 auto format_float(Float 48 --num_digits; 49 ++exp; 50 } 51 buf.try_resize(num_digits); 52 } 53 return exp; 54 } 55 56 +// See https://bugzilla.mozilla.org/show_bug.cgi?id=1717448#c2 for the reason 57 +// we're doing this. This copied from and should be kept in sync with 58 +// PrintfTarget::cvt_f in Gecko's mozglue/misc/Printf.cpp. 59 +template <typename Float> 60 +auto format_float_gecko(Float value, int precision, format_specs specs, 61 + buffer<char>& buf) -> int { 62 + FMT_ASSERT(detail::isfinite(value), 63 + "Non-finite values are to be handled ahead of calling this"); 64 + using double_conversion::DoubleToStringConverter; 65 + using DTSC = DoubleToStringConverter; 66 + // Printf.cpp seem to use UNIQUE_ZERO here, but then adds its own `-` further 67 + // in the code. 68 + char e = specs.upper() ? 'E' : 'e'; 69 + DTSC converter(DTSC::NO_TRAILING_ZERO | 70 + DTSC::EMIT_POSITIVE_EXPONENT_SIGN, 71 + "inf", "nan", e, 0, 0, 4, 0, 2); 72 + buf.try_resize(64); 73 + double_conversion::StringBuilder builder(buf.data(), buf.size()); 74 + // Negative precision defaults to 6. 75 + if (precision == -1) { precision = 6; } 76 + bool success; 77 + if (specs.type() == presentation_type::exp) { 78 + success = converter.ToExponential(value, precision, &builder); 79 + } else if (specs.type() == presentation_type::fixed) { 80 + success = converter.ToFixed(value, precision, &builder); 81 + } else if (specs.type() == presentation_type::general || 82 + specs.type() == presentation_type::none) { 83 + // "If an explicit precision is zero, it shall be taken as 1." 84 + success = converter.ToPrecision(value, precision ? precision : 1, &builder); 85 + } else { 86 + FMT_ASSERT(false, "Unhandled"); 87 + } 88 + FMT_ASSERT(success, ""); 89 + buf.try_resize(builder.position()); 90 + return builder.position(); 91 +} 92 + 93 template <typename Char, typename OutputIt, typename T> 94 FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, 95 locale_ref loc) -> OutputIt { 96 // Use signbit because value < 0 is false for NaN. 97 sign s = detail::signbit(value) ? sign::minus : specs.sign(); 98 99 if (!detail::isfinite(value)) 100 return write_nonfinite<Char>(out, detail::isnan(value), specs, s); 101 102 if (specs.align() == align::numeric && s != sign::none) { 103 *out++ = detail::getsign<Char>(s); 104 s = sign::none; 105 if (specs.width != 0) --specs.width; 106 } 107 108 int precision = specs.precision; 109 - if (precision < 0) { 110 - if (specs.type() != presentation_type::none) { 111 - precision = 6; 112 - } else if (is_fast_float<T>::value && !is_constant_evaluated()) { 113 - // Use Dragonbox for the shortest format. 114 - using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>; 115 - auto dec = dragonbox::to_decimal(static_cast<floaty>(value)); 116 - return write_float<Char>(out, dec, specs, s, loc); 117 - } 118 - } 119 - 120 memory_buffer buffer; 121 if (specs.type() == presentation_type::hexfloat) { 122 if (s != sign::none) buffer.push_back(detail::getsign<char>(s)); 123 format_hexfloat(convert_float(value), specs, buffer); 124 return write_bytes<Char, align::right>(out, {buffer.data(), buffer.size()}, 125 specs); 126 } 127 128 if (specs.type() == presentation_type::exp) { 129 - if (precision == max_value<int>()) 130 - report_error("number is too big"); 131 - else 132 - ++precision; 133 if (specs.precision != 0) specs.set_alt(); 134 } else if (specs.type() == presentation_type::fixed) { 135 if (specs.precision != 0) specs.set_alt(); 136 } else if (precision == 0) { 137 precision = 1; 138 } 139 - int exp = format_float(convert_float(value), precision, specs, 140 - std::is_same<T, float>(), buffer); 141 142 specs.precision = precision; 143 - auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp}; 144 - return write_float<Char>(out, f, specs, s, loc); 145 + value = abs(value); 146 + format_float_gecko(convert_float(value), precision, specs, buffer); 147 + size_t size = buffer.size(); 148 + if (s != sign::none) { 149 + // Space for the sign, this influences the padding calculations below. 150 + size++; 151 + // If padding with zeros, the sign goes here, e.g. -0004.3 152 + if (*specs.fill<Char>() == '0') { 153 + *out++ = detail::getsign<Char>(s); 154 + } 155 + } 156 + return write_padded<Char, align::right>( 157 + out, specs, size, size, [s, &buffer, specs](reserve_iterator<OutputIt> it) { 158 + // If padding with something other than zeros, the sign goes here, e.g. 159 + // ' -4.3' (quotes added for clarity). 160 + if (s != sign::none) { 161 + if (*specs.fill<Char>() != '0') { 162 + *it++ = detail::getsign<Char>(s); 163 + } 164 + } 165 + const char* data = buffer.data(); 166 + return copy<Char>(data, data + buffer.size(), it); 167 + }); 168 } 169 170 template <typename Char, typename OutputIt, typename T, 171 FMT_ENABLE_IF(is_floating_point<T>::value)> 172 FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, 173 locale_ref loc = {}) -> OutputIt { 174 return specs.localized() && write_loc(out, value, specs, loc) 175 ? out 176 @@ -3513,18 +3563,17 @@ FMT_CONSTEXPR20 auto write(OutputIt out, 177 178 constexpr auto specs = format_specs(); 179 using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>; 180 using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint; 181 floaty_uint mask = exponent_mask<floaty>(); 182 if ((bit_cast<floaty_uint>(value) & mask) == mask) 183 return write_nonfinite<Char>(out, std::isnan(value), specs, s); 184 185 - auto dec = dragonbox::to_decimal(static_cast<floaty>(value)); 186 - return write_float<Char>(out, dec, specs, s, {}); 187 + return write_float<Char>(out, value, specs, {}); 188 } 189 190 template <typename Char, typename OutputIt, typename T, 191 FMT_ENABLE_IF(is_floating_point<T>::value && 192 !is_fast_float<T>::value)> 193 inline auto write(OutputIt out, T value) -> OutputIt { 194 return write<Char>(out, value, format_specs()); 195 }