Skip to content

Commit a86d30e

Browse files
fix: Implement safe numeric casting and error handling for numeric conversions
Signed-off-by: FrozenlemonTee <1115306170@qq.com>
1 parent b9af9ca commit a86d30e

2 files changed

Lines changed: 63 additions & 3 deletions

File tree

src/operations/operators.cppm

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,16 +333,17 @@ constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs)
333333

334334
auto const assigned_common = out->load();
335335
if constexpr (std::same_as<lhs_value_policy, policy::value::checked> &&
336-
std::integral<lhs_rep> && std::integral<common_rep>) {
336+
std::integral<lhs_rep>) {
337337
if (auto const kind =
338-
policy::details::narrow_integral_error<lhs_rep>(assigned_common);
338+
policy::details::narrow_numeric_error<lhs_rep>(assigned_common);
339339
kind.has_value()) {
340340
return std::unexpected(
341341
policy::details::to_error_payload<ErrorPayload>(*kind));
342342
}
343343
}
344344

345-
auto const assigned_rep = static_cast<lhs_rep>(assigned_common);
345+
auto const assigned_rep =
346+
policy::details::safe_numeric_cast<lhs_rep>(assigned_common);
346347
lhs.store(underlying::traits<lhs_value_type>::from_rep(assigned_rep));
347348
return out;
348349
}

src/policy/impl.cppm

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module;
22
#include <atomic>
3+
#include <cmath>
34
#include <cstdint>
45
#include <concepts>
56
#include <exception>
@@ -574,6 +575,64 @@ constexpr auto narrow_integral_error(SrcRep value)
574575
return std::nullopt;
575576
}
576577
}
578+
579+
template <typename DestRep, typename SrcRep>
580+
constexpr auto narrow_numeric_error(SrcRep value)
581+
-> std::optional<error::kind> {
582+
using dest_type = std::remove_cv_t<DestRep>;
583+
using src_type = std::remove_cv_t<SrcRep>;
584+
585+
if constexpr (std::integral<dest_type> && std::integral<src_type>) {
586+
return narrow_integral_error<dest_type>(value);
587+
} else if constexpr (std::integral<dest_type> &&
588+
std::floating_point<src_type>) {
589+
if (std::isnan(value)) {
590+
return error::kind::domain_error;
591+
}
592+
if (std::isinf(value)) {
593+
return value < static_cast<src_type>(0) ? error::kind::underflow
594+
: error::kind::overflow;
595+
}
596+
597+
auto const normalized = static_cast<long double>(value);
598+
auto const min_value =
599+
static_cast<long double>(std::numeric_limits<dest_type>::lowest());
600+
auto const max_value =
601+
static_cast<long double>(std::numeric_limits<dest_type>::max());
602+
603+
if (normalized < min_value) {
604+
return error::kind::underflow;
605+
}
606+
if (normalized > max_value) {
607+
return error::kind::overflow;
608+
}
609+
return std::nullopt;
610+
} else {
611+
static_cast<void>(value);
612+
return std::nullopt;
613+
}
614+
}
615+
616+
template <typename DestRep, typename SrcRep>
617+
constexpr auto safe_numeric_cast(SrcRep value) noexcept -> DestRep {
618+
using dest_type = std::remove_cv_t<DestRep>;
619+
using src_type = std::remove_cv_t<SrcRep>;
620+
621+
if constexpr (std::integral<dest_type> && std::floating_point<src_type>) {
622+
if (auto const kind = narrow_numeric_error<dest_type>(value);
623+
kind.has_value()) {
624+
if (*kind == error::kind::overflow) {
625+
return std::numeric_limits<dest_type>::max();
626+
}
627+
if (*kind == error::kind::underflow) {
628+
return std::numeric_limits<dest_type>::lowest();
629+
}
630+
return dest_type{};
631+
}
632+
}
633+
634+
return static_cast<dest_type>(value);
635+
}
577636
} // namespace details
578637

579638
template <operations::operation OpTag, typename CommonRep,

0 commit comments

Comments
 (0)