195 lines
7.5 KiB
Diff
195 lines
7.5 KiB
Diff
diff --git a/include/fmt/format.h b/include/fmt/format.h
|
|
--- a/include/fmt/format.h
|
|
+++ b/include/fmt/format.h
|
|
@@ -28,16 +28,18 @@
|
|
of this Software are embedded into a machine-executable object form of such
|
|
source code, you may redistribute such embedded portions in such object form
|
|
without including the above copyright and permission notices.
|
|
*/
|
|
|
|
#ifndef FMT_FORMAT_H_
|
|
#define FMT_FORMAT_H_
|
|
|
|
+#include "double-conversion/double-to-string.h"
|
|
+
|
|
#ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
|
|
# define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
|
|
# define FMT_REMOVE_TRANSITIVE_INCLUDES
|
|
#endif
|
|
|
|
#include "base.h"
|
|
|
|
#ifndef FMT_MODULE
|
|
@@ -3141,16 +3143,23 @@ constexpr auto fractional_part_rounding_
|
|
return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7"
|
|
U"\x800001ae\x8000002b"[index];
|
|
}
|
|
|
|
template <typename Float>
|
|
FMT_CONSTEXPR20 auto format_float(Float value, int precision,
|
|
const format_specs& specs, bool binary32,
|
|
buffer<char>& buf) -> int {
|
|
+ // GCC and old clang seem to hit this even though this function isn't called.
|
|
+ #ifdef __clang__
|
|
+ #if __clang_major__ >= 17
|
|
+ static_assert(false,
|
|
+ "This method is not to be used in Gecko, use format_float_gecko");
|
|
+ #endif
|
|
+ #endif
|
|
// float is passed as double to reduce the number of instantiations.
|
|
static_assert(!std::is_same<Float, float>::value, "");
|
|
auto converted_value = convert_float(value);
|
|
|
|
const bool fixed = specs.type() == presentation_type::fixed;
|
|
if (value == 0) {
|
|
if (precision <= 0 || !fixed) {
|
|
buf.push_back('0');
|
|
@@ -3436,68 +3445,109 @@ FMT_CONSTEXPR20 auto format_float(Float
|
|
--num_digits;
|
|
++exp;
|
|
}
|
|
buf.try_resize(num_digits);
|
|
}
|
|
return exp;
|
|
}
|
|
|
|
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1717448#c2 for the reason
|
|
+// we're doing this. This copied from and should be kept in sync with
|
|
+// PrintfTarget::cvt_f in Gecko's mozglue/misc/Printf.cpp.
|
|
+template <typename Float>
|
|
+auto format_float_gecko(Float value, int precision, format_specs specs,
|
|
+ buffer<char>& buf) -> int {
|
|
+ FMT_ASSERT(detail::isfinite(value),
|
|
+ "Non-finite values are to be handled ahead of calling this");
|
|
+ using double_conversion::DoubleToStringConverter;
|
|
+ using DTSC = DoubleToStringConverter;
|
|
+ // Printf.cpp seem to use UNIQUE_ZERO here, but then adds its own `-` further
|
|
+ // in the code.
|
|
+ char e = specs.upper() ? 'E' : 'e';
|
|
+ DTSC converter(DTSC::NO_TRAILING_ZERO |
|
|
+ DTSC::EMIT_POSITIVE_EXPONENT_SIGN,
|
|
+ "inf", "nan", e, 0, 0, 4, 0, 2);
|
|
+ buf.try_resize(64);
|
|
+ double_conversion::StringBuilder builder(buf.data(), buf.size());
|
|
+ // Negative precision defaults to 6.
|
|
+ if (precision == -1) { precision = 6; }
|
|
+ bool success;
|
|
+ if (specs.type() == presentation_type::exp) {
|
|
+ success = converter.ToExponential(value, precision, &builder);
|
|
+ } else if (specs.type() == presentation_type::fixed) {
|
|
+ success = converter.ToFixed(value, precision, &builder);
|
|
+ } else if (specs.type() == presentation_type::general ||
|
|
+ specs.type() == presentation_type::none) {
|
|
+ // "If an explicit precision is zero, it shall be taken as 1."
|
|
+ success = converter.ToPrecision(value, precision ? precision : 1, &builder);
|
|
+ } else {
|
|
+ FMT_ASSERT(false, "Unhandled");
|
|
+ }
|
|
+ FMT_ASSERT(success, "");
|
|
+ buf.try_resize(builder.position());
|
|
+ return builder.position();
|
|
+}
|
|
+
|
|
template <typename Char, typename OutputIt, typename T>
|
|
FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
|
|
locale_ref loc) -> OutputIt {
|
|
// Use signbit because value < 0 is false for NaN.
|
|
sign s = detail::signbit(value) ? sign::minus : specs.sign();
|
|
|
|
if (!detail::isfinite(value))
|
|
return write_nonfinite<Char>(out, detail::isnan(value), specs, s);
|
|
|
|
if (specs.align() == align::numeric && s != sign::none) {
|
|
*out++ = detail::getsign<Char>(s);
|
|
s = sign::none;
|
|
if (specs.width != 0) --specs.width;
|
|
}
|
|
|
|
int precision = specs.precision;
|
|
- if (precision < 0) {
|
|
- if (specs.type() != presentation_type::none) {
|
|
- precision = 6;
|
|
- } else if (is_fast_float<T>::value && !is_constant_evaluated()) {
|
|
- // Use Dragonbox for the shortest format.
|
|
- using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
|
|
- auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
|
|
- return write_float<Char>(out, dec, specs, s, loc);
|
|
- }
|
|
- }
|
|
-
|
|
memory_buffer buffer;
|
|
if (specs.type() == presentation_type::hexfloat) {
|
|
if (s != sign::none) buffer.push_back(detail::getsign<char>(s));
|
|
format_hexfloat(convert_float(value), specs, buffer);
|
|
return write_bytes<Char, align::right>(out, {buffer.data(), buffer.size()},
|
|
specs);
|
|
}
|
|
|
|
if (specs.type() == presentation_type::exp) {
|
|
- if (precision == max_value<int>())
|
|
- report_error("number is too big");
|
|
- else
|
|
- ++precision;
|
|
if (specs.precision != 0) specs.set_alt();
|
|
} else if (specs.type() == presentation_type::fixed) {
|
|
if (specs.precision != 0) specs.set_alt();
|
|
} else if (precision == 0) {
|
|
precision = 1;
|
|
}
|
|
- int exp = format_float(convert_float(value), precision, specs,
|
|
- std::is_same<T, float>(), buffer);
|
|
|
|
specs.precision = precision;
|
|
- auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
|
|
- return write_float<Char>(out, f, specs, s, loc);
|
|
+ value = abs(value);
|
|
+ format_float_gecko(convert_float(value), precision, specs, buffer);
|
|
+ size_t size = buffer.size();
|
|
+ if (s != sign::none) {
|
|
+ // Space for the sign, this influences the padding calculations below.
|
|
+ size++;
|
|
+ // If padding with zeros, the sign goes here, e.g. -0004.3
|
|
+ if (*specs.fill<Char>() == '0') {
|
|
+ *out++ = detail::getsign<Char>(s);
|
|
+ }
|
|
+ }
|
|
+ return write_padded<Char, align::right>(
|
|
+ out, specs, size, size, [s, &buffer, specs](reserve_iterator<OutputIt> it) {
|
|
+ // If padding with something other than zeros, the sign goes here, e.g.
|
|
+ // ' -4.3' (quotes added for clarity).
|
|
+ if (s != sign::none) {
|
|
+ if (*specs.fill<Char>() != '0') {
|
|
+ *it++ = detail::getsign<Char>(s);
|
|
+ }
|
|
+ }
|
|
+ const char* data = buffer.data();
|
|
+ return copy<Char>(data, data + buffer.size(), it);
|
|
+ });
|
|
}
|
|
|
|
template <typename Char, typename OutputIt, typename T,
|
|
FMT_ENABLE_IF(is_floating_point<T>::value)>
|
|
FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
|
|
locale_ref loc = {}) -> OutputIt {
|
|
return specs.localized() && write_loc(out, value, specs, loc)
|
|
? out
|
|
@@ -3513,18 +3563,17 @@ FMT_CONSTEXPR20 auto write(OutputIt out,
|
|
|
|
constexpr auto specs = format_specs();
|
|
using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
|
|
using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
|
|
floaty_uint mask = exponent_mask<floaty>();
|
|
if ((bit_cast<floaty_uint>(value) & mask) == mask)
|
|
return write_nonfinite<Char>(out, std::isnan(value), specs, s);
|
|
|
|
- auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
|
|
- return write_float<Char>(out, dec, specs, s, {});
|
|
+ return write_float<Char>(out, value, specs, {});
|
|
}
|
|
|
|
template <typename Char, typename OutputIt, typename T,
|
|
FMT_ENABLE_IF(is_floating_point<T>::value &&
|
|
!is_fast_float<T>::value)>
|
|
inline auto write(OutputIt out, T value) -> OutputIt {
|
|
return write<Char>(out, value, format_specs());
|
|
}
|