From 903db017c074bfaba98005c5c2d7b9dab446851c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 25 Mar 2026 14:10:15 -0400 Subject: [PATCH] Take another strike at saturation. --- ClockReceiver/ClockReceiver.hpp | 55 ++++++++++++++++----------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index 65efdb886..8289cb97b 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -83,23 +83,37 @@ public: template requires std::integral || std::floating_point constexpr Type as() const { - const auto value = get(); + // TODO: adopt std::saturate_cast if/when this is a C++26 project. - if constexpr (sizeof(Type) == sizeof(IntType) && std::is_integral_v) { - if constexpr (std::is_same_v) { - return value; - } else if constexpr (std::is_signed_v) { - // Both integers are the same size, but a signed result is being asked for - // from an unsigned original. - return value > Type(std::numeric_limits::max()) ? - Type(std::numeric_limits::max()) : Type(value); + const IntType value = get(); + if constexpr (std::is_floating_point_v) { + // Floating point: the caller takes responsibility for loss of precision; + // acceptable range can be assumed. + return Type(value); + } else { + assert(std::is_signed_v); + + if constexpr (std::is_signed_v) { + // Casting to another signed type; either it's large enough that range can't be exceeded, + // or it's a lesser range that can safely be clamped to. + if constexpr (sizeof(Type) >= sizeof(IntType)) { + return Type(value); + } else { + return Type(std::clamp(value, IntType(std::numeric_limits::min()), IntType(std::numeric_limits::max()))); + } } else { - // An unsigned result is being asked for from a signed original. - return value < 0 ? 0 : Type(value); + // Casting to an unsigned type: negative numbers map straight to 0, everything positive + // either unambiguously fits, or can be subject to an in-IntType min. + if(value <= 0) { + return 0; + } + if constexpr (sizeof(Type) >= sizeof(IntType)) { + return Type(value); + } else { + return Type(std::min(value, IntType(std::numeric_limits::max()))); + } } } - - return Type(std::clamp(length_, low, high)); } /// @returns The underlying int, in its native form, potentially scaled upward. @@ -172,21 +186,6 @@ public: private: IntType length_; - - template - static consteval bool can_represent(const Type x) { - return std::numeric_limits::min() <= x && std::numeric_limits::max() >= x; - } - - template - static constexpr IntType low = - can_represent(std::numeric_limits::min()) ? - IntType(std::numeric_limits::min()) : std::numeric_limits::min(); - - template - static constexpr IntType high = - can_represent(std::numeric_limits::max()) ? - IntType(std::numeric_limits::max()) : std::numeric_limits::max(); }; /// Reasons Clocks into being a count of querter cycles, building half- and whole-cycles from there.