Source code

Revision control

Copy as Markdown

Other Tools

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__ > 8
+ 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;
}
+// 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>
+FMT_CONSTEXPR20 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());
}