mirror of
https://github.com/fmtlib/fmt.git
synced 2026-11-09 14:50:24 +08:00
Simplify float to integral duration_cast and its tests
Compute the upper bound as 2^digits with std::ldexp instead of static_cast<FromRep>(max) + 1, which is only exact when rounding to nearest: with FE_UPWARD in an unoptimized build the bound became 2^63 + 2048 and a count of 2^63 reached the undefined conversion. Truncate the count once and use it for both the check and the cast. Skip the checks that need safe duration casts when FMT_SAFE_DURATION_CAST is 0, where the NaN time point check failed. Drop the 1e300 time point and two bool assertions, which don't catch anything the others don't, and test max + 1 and NaN directly.
This commit is contained in:
parent
4ed3535092
commit
7690aa5574
@ -533,16 +533,13 @@ template <typename To, typename FromRep, typename FromPeriod,
|
||||
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
||||
#if FMT_SAFE_DURATION_CAST
|
||||
using to_rep = typename To::rep;
|
||||
auto count = detail::duration_cast<
|
||||
std::chrono::duration<FromRep, typename To::period>>(from)
|
||||
.count();
|
||||
// The conversion truncates toward zero so check the truncated value.
|
||||
// max + 1 is a power of 2 so the cast to FromRep is exact even when max
|
||||
// itself is rounded up by the conversion; the comparison is false for NaN.
|
||||
auto upper = static_cast<FromRep>(max_value<to_rep>()) + 1;
|
||||
using float_duration = std::chrono::duration<FromRep, typename To::period>;
|
||||
auto count = std::trunc(detail::duration_cast<float_duration>(from).count());
|
||||
// The conversion truncates, so check the truncated count against
|
||||
// [min, max + 1) where max + 1 = 2^digits. NaN fails the comparison.
|
||||
auto lower = static_cast<FromRep>((std::numeric_limits<to_rep>::min)());
|
||||
auto truncated = std::trunc(count);
|
||||
if (!(truncated >= lower && truncated < upper)) throw_duration_error();
|
||||
auto upper = std::ldexp(FromRep(1), std::numeric_limits<to_rep>::digits);
|
||||
if (!(count >= lower && count < upper)) throw_duration_error();
|
||||
return To(static_cast<to_rep>(count));
|
||||
#else
|
||||
return std::chrono::duration_cast<To>(from);
|
||||
|
||||
@ -1053,9 +1053,8 @@ TEST(chrono_test, out_of_range) {
|
||||
// A floating-point day count that doesn't fit in int.
|
||||
auto fd = std::chrono::duration<double>(1e300);
|
||||
EXPECT_THROW((void)fmt::format("{:%j}", fd), fmt::format_error);
|
||||
// A floating-point time point whose seconds don't fit in time_t.
|
||||
auto ftp = sys_time<std::chrono::duration<double>>(fd);
|
||||
EXPECT_THROW((void)fmt::format("{:%T}", ftp), fmt::format_error);
|
||||
if (!FMT_SAFE_DURATION_CAST) return;
|
||||
// Floating-point time points whose seconds don't fit in time_t.
|
||||
auto fnan = sys_time<std::chrono::duration<double>>(
|
||||
std::chrono::duration<double>(std::numeric_limits<double>::quiet_NaN()));
|
||||
EXPECT_THROW((void)fmt::format("{:%T}", fnan), fmt::format_error);
|
||||
@ -1083,17 +1082,21 @@ TEST(chrono_test, float_to_integral_duration_cast) {
|
||||
max);
|
||||
// Any nonzero value converts to true.
|
||||
using bool_duration = std::chrono::duration<bool>;
|
||||
EXPECT_FALSE(fmt::detail::duration_cast<bool_duration>(fsec(0)).count());
|
||||
EXPECT_TRUE(fmt::detail::duration_cast<bool_duration>(fsec(2)).count());
|
||||
EXPECT_TRUE(fmt::detail::duration_cast<bool_duration>(fsec(-1)).count());
|
||||
if (FMT_SAFE_DURATION_CAST) {
|
||||
EXPECT_THROW(
|
||||
fmt::detail::duration_cast<std::chrono::duration<unsigned>>(fsec(-1)),
|
||||
fmt::format_error);
|
||||
EXPECT_THROW(
|
||||
fmt::detail::duration_cast<std::chrono::duration<int>>(fsec(min - 1.0)),
|
||||
fmt::format_error);
|
||||
}
|
||||
if (!FMT_SAFE_DURATION_CAST) return;
|
||||
EXPECT_THROW(
|
||||
fmt::detail::duration_cast<std::chrono::duration<unsigned>>(fsec(-1)),
|
||||
fmt::format_error);
|
||||
EXPECT_THROW(
|
||||
fmt::detail::duration_cast<std::chrono::duration<int>>(fsec(min - 1.0)),
|
||||
fmt::format_error);
|
||||
// max + 1, which is also what max rounds to in the source type.
|
||||
EXPECT_THROW(fmt::detail::duration_cast<std::chrono::duration<long long>>(
|
||||
fsec(9223372036854775808.0)),
|
||||
fmt::format_error);
|
||||
EXPECT_THROW(fmt::detail::duration_cast<std::chrono::duration<int>>(
|
||||
fsec(std::numeric_limits<double>::quiet_NaN())),
|
||||
fmt::format_error);
|
||||
}
|
||||
|
||||
struct custom_rep {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user