mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-31 19:24:48 +02:00
Fix overflow for chrono durations (#2722)
This commit is contained in:
@@ -1470,14 +1470,23 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the number of fractional digits in the range [0, 18] according to the
|
// Counts the number of fractional digits in the range [0, 18] according to the
|
||||||
// C++20 spec. If more than 18 fractional digits are required then returns 6 for
|
// C++20 spec. If more than 18 fractional digits are required then returns 6 for
|
||||||
// microseconds precision.
|
// microseconds precision.
|
||||||
constexpr int count_fractional_digits(long long num, long long den, int n = 0) {
|
template <long long Num, long long Den, int N = 0,
|
||||||
return num % den == 0
|
bool Enabled =
|
||||||
? n
|
(N < 19) && (Num <= std::numeric_limits<long long>::max() / 10)>
|
||||||
: (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1));
|
struct count_fractional_digits {
|
||||||
}
|
static constexpr int value =
|
||||||
|
Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Base case that doesn't instantiate any more templates
|
||||||
|
// in order to avoid overflow.
|
||||||
|
template <long long Num, long long Den, int N>
|
||||||
|
struct count_fractional_digits<Num, Den, N, false> {
|
||||||
|
static constexpr int value = (Num % Den == 0) ? N : 6;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr long long pow10(std::uint32_t n) {
|
constexpr long long pow10(std::uint32_t n) {
|
||||||
return n == 0 ? 1 : 10 * pow10(n - 1);
|
return n == 0 ? 1 : 10 * pow10(n - 1);
|
||||||
@@ -1666,7 +1675,8 @@ struct chrono_formatter {
|
|||||||
template <typename Duration> void write_fractional_seconds(Duration d) {
|
template <typename Duration> void write_fractional_seconds(Duration d) {
|
||||||
FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
|
FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
|
||||||
constexpr auto num_fractional_digits =
|
constexpr auto num_fractional_digits =
|
||||||
count_fractional_digits(Duration::period::num, Duration::period::den);
|
count_fractional_digits<Duration::period::num,
|
||||||
|
Duration::period::den>::value;
|
||||||
|
|
||||||
using subsecond_precision = std::chrono::duration<
|
using subsecond_precision = std::chrono::duration<
|
||||||
typename std::common_type<typename Duration::rep,
|
typename std::common_type<typename Duration::rep,
|
||||||
@@ -1769,8 +1779,8 @@ struct chrono_formatter {
|
|||||||
|
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard) {
|
||||||
if (std::is_floating_point<rep>::value) {
|
if (std::is_floating_point<rep>::value) {
|
||||||
auto num_fractional_digits =
|
constexpr auto num_fractional_digits =
|
||||||
count_fractional_digits(Period::num, Period::den);
|
count_fractional_digits<Period::num, Period::den>::value;
|
||||||
auto buf = memory_buffer();
|
auto buf = memory_buffer();
|
||||||
format_to(std::back_inserter(buf), runtime("{:.{}f}"),
|
format_to(std::back_inserter(buf), runtime("{:.{}f}"),
|
||||||
std::fmod(val * static_cast<rep>(Period::num) /
|
std::fmod(val * static_cast<rep>(Period::num) /
|
||||||
|
@@ -623,6 +623,10 @@ TEST(chrono_test, cpp20_duration_subsecond_support) {
|
|||||||
// fixed precision, and print zeros even if there is no fractional part.
|
// fixed precision, and print zeros even if there is no fractional part.
|
||||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
|
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
|
||||||
"07.000000");
|
"07.000000");
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 3>>(1)),
|
||||||
|
"00.333333");
|
||||||
|
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)),
|
||||||
|
"00.142857");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
|
Reference in New Issue
Block a user