diff --git a/include/fmt/base.h b/include/fmt/base.h index 2e605a7350c7..da90254672a6 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -2422,14 +2422,11 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; struct { const Char*& begin; - dynamic_format_specs& specs; + format_specs& specs; type arg_type; FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { - if (!in(arg_type, set)) { - if (arg_type == type::none_type) return begin; - report_error("invalid format specifier"); - } + if (!in(arg_type, set)) report_error("invalid format specifier"); specs.type = pres_type; return begin + 1; } @@ -2445,35 +2442,23 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, ++begin; break; case '+': - case '-': + FMT_FALLTHROUGH; case ' ': - if (arg_type == type::none_type) return begin; + specs.sign = c == ' ' ? sign::space : sign::plus; + FMT_FALLTHROUGH; + case '-': enter_state(state::sign, in(arg_type, sint_set | float_set)); - switch (c) { - case '+': - specs.sign = sign::plus; - break; - case '-': - specs.sign = sign::minus; - break; - case ' ': - specs.sign = sign::space; - break; - } ++begin; break; case '#': - if (arg_type == type::none_type) return begin; enter_state(state::hash, is_arithmetic_type(arg_type)); specs.alt = true; ++begin; break; case '0': enter_state(state::zero); - if (!is_arithmetic_type(arg_type)) { - if (arg_type == type::none_type) return begin; + if (!is_arithmetic_type(arg_type)) report_error("format specifier requires numeric argument"); - } if (specs.align == align::none) { // Ignore 0 if align is specified for compatibility with std::format. specs.align = align::numeric; @@ -2495,14 +2480,12 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); break; case '.': - if (arg_type == type::none_type) return begin; enter_state(state::precision, in(arg_type, float_set | string_set | cstring_set)); begin = parse_precision(begin, end, specs.precision, specs.precision_ref, ctx); break; case 'L': - if (arg_type == type::none_type) return begin; enter_state(state::locale, is_arithmetic_type(arg_type)); specs.localized = true; ++begin; diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 76af8c24ca23..1fc3f1f5f5f8 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -649,7 +649,7 @@ FMT_CONSTEXPR inline auto get_units() -> const char* { if (std::is_same::value) return "fs"; if (std::is_same::value) return "ps"; if (std::is_same::value) return "ns"; - if (std::is_same::value) return "µs"; + if (std::is_same::value) return use_utf8() ? "µs" : "us"; if (std::is_same::value) return "ms"; if (std::is_same::value) return "cs"; if (std::is_same::value) return "ds"; diff --git a/include/fmt/format.h b/include/fmt/format.h index b09627505b01..8063f6c72f44 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2325,7 +2325,6 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { return format_decimal(out, abs_value, num_digits); } -// DEPRECATED! template FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, format_specs& specs) -> const Char* { @@ -3175,11 +3174,10 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, buffer& buf) -> int { // float is passed as double to reduce the number of instantiations. static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); auto converted_value = convert_float(value); const bool fixed = specs.type == presentation_type::fixed; - if (value <= 0) { // <= instead of == to silence a warning. + if (value == 0) { if (precision <= 0 || !fixed) { buf.push_back('0'); return 0; @@ -3204,16 +3202,6 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, exp = static_cast(e); if (e > exp) ++exp; // Compute ceil. dragon_flags = dragon::fixup; - } else if (precision < 0) { - // Use Dragonbox for the shortest format. - if (binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(appender(buf), dec.significand); - return dec.exponent; } else { // Extract significand bits and exponent bits. using info = dragonbox::float_info; @@ -3482,13 +3470,8 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, template FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, locale_ref loc) -> OutputIt { - sign_t sign = specs.sign; - if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. - sign = sign::minus; - value = -value; - } else if (sign == sign::minus) { - sign = sign::none; - } + // Use signbit because value < 0 is false for NaN. + sign_t sign = detail::signbit(value) ? sign::minus : specs.sign; if (!detail::isfinite(value)) return write_nonfinite(out, detail::isnan(value), specs, sign); @@ -3499,6 +3482,18 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, 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::value && !is_constant_evaluated()) { + // Use Dragonbox for the shortest format. + using floaty = conditional_t= sizeof(double), double, float>; + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, sign, loc); + } + } + memory_buffer buffer; if (specs.type == presentation_type::hexfloat) { if (sign) buffer.push_back(detail::sign(sign)); @@ -3507,9 +3502,6 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, specs); } - int precision = specs.precision >= 0 || specs.type == presentation_type::none - ? specs.precision - : 6; if (specs.type == presentation_type::exp) { if (precision == max_value()) report_error("number is too big"); @@ -3521,7 +3513,6 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, } else if (precision == 0) { precision = 1; } - int exp = format_float(convert_float(value), precision, specs, std::is_same(), buffer); @@ -3546,14 +3537,10 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { if (is_constant_evaluated()) return write(out, value, format_specs()); if (const_check(!is_supported_floating_point(value))) return out; - auto sign = sign_t::none; - if (detail::signbit(value)) { - sign = sign::minus; - value = -value; - } + auto sign = detail::signbit(value) ? sign::minus : sign_t::none; constexpr auto specs = format_specs(); - using floaty = conditional_t::value, double, T>; + using floaty = conditional_t= sizeof(double), double, float>; using floaty_uint = typename dragonbox::float_info::carrier_uint; floaty_uint mask = exponent_mask(); if ((bit_cast(value) & mask) == mask) @@ -4090,12 +4077,16 @@ template struct nested_formatter { FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) -> decltype(ctx.begin()) { - auto specs = detail::dynamic_format_specs(); - auto it = parse_format_specs(ctx.begin(), ctx.end(), specs, ctx, - detail::type::none_type); - width_ = specs.width; + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + auto specs = format_specs(); + it = detail::parse_align(it, end, specs); fill_ = specs.fill; align_ = specs.align; + Char c = *it; + auto width_ref = detail::arg_ref(); + if ((c >= '0' && c <= '9') || c == '{') + it = detail::parse_dynamic_spec(it, end, width_, width_ref, ctx); ctx.advance_to(it); return formatter_.parse(ctx); } diff --git a/test/base-test.cc b/test/base-test.cc index c719d7443f13..8afd3783a3d4 100644 --- a/test/base-test.cc +++ b/test/base-test.cc @@ -519,7 +519,7 @@ TEST(base_test, constexpr_parse_format_specs) { static_assert(parse_test_specs("<").align == fmt::align::left, ""); static_assert(parse_test_specs("*^").fill.get() == '*', ""); static_assert(parse_test_specs("+").sign == fmt::sign::plus, ""); - static_assert(parse_test_specs("-").sign == fmt::sign::minus, ""); + static_assert(parse_test_specs("-").sign == fmt::sign::none, ""); static_assert(parse_test_specs(" ").sign == fmt::sign::space, ""); static_assert(parse_test_specs("#").alt, ""); static_assert(parse_test_specs("0").align == fmt::align::numeric, "");