// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // numeric_state.hpp // Numeric state-management system // // This file defines the state-management system shared by numeric types such as // multi-precision integers and floating-point numbers. // It expresses IEEE 754 special values and various states arising in numerical computation. #ifndef SANGI_NUMERIC_STATE_HPP #define SANGI_NUMERIC_STATE_HPP #include #include #include #include namespace sangi { /** * @brief Class describing properties of numeric states. * * Provides methods that classify numeric states and test their properties. */ class NumericStateTraits { public: /** * @brief Determine whether the state is NaN * @param state state to test * @return true if NaN */ static constexpr bool isNaN(NumericState state) { return state == NumericState::NaN; } /** * @brief Determine whether the state is infinite * @param state state to test * @return true if infinite */ static constexpr bool isInfinite(NumericState state) { return state == NumericState::PositiveInfinity || state == NumericState::NegativeInfinity || state == NumericState::ComplexInfinity; } /** * @brief Determine whether the state failed to converge * @param state state to test * @return true if not converged */ static constexpr bool isNotConverged(NumericState state) { return state == NumericState::NotConverged; } /** * @brief Determine whether the state is divergent * @param state state to test * @return true if divergent */ static constexpr bool isDivergent(NumericState state) { return state == NumericState::Divergent || state == NumericState::NotConverged || state == NumericState::Oscillating || state == NumericState::SlowConvergence; } /** * @brief Determine whether the state is special * @param state state to test * @return true if a special state (anything other than Normal) */ static constexpr bool isSpecial(NumericState state) { return state != NumericState::Normal; } /** * @brief Get the string representation of a state * @param state state * @return string representation of the state */ static std::string toString(NumericState state) { switch (state) { case NumericState::Normal: return "Normal"; case NumericState::NaN: return "NaN"; case NumericState::PositiveInfinity: return "Infinity"; case NumericState::NegativeInfinity: return "-Infinity"; case NumericState::ComplexInfinity: return "ComplexInfinity"; case NumericState::Overflow: return "Overflow"; case NumericState::Underflow: return "Underflow"; case NumericState::Interrupted: return "Interrupted"; case NumericState::PrecisionLimit: return "PrecisionLimit"; case NumericState::PositiveZero: return "+Zero"; case NumericState::NegativeZero: return "-Zero"; case NumericState::Subnormal: return "Subnormal"; case NumericState::NotConverged: return "NotConverged"; case NumericState::Divergent: return "Divergent"; case NumericState::Oscillating: return "Oscillating"; case NumericState::SlowConvergence: return "SlowConvergence"; case NumericState::TruncatedConvergence: return "TruncatedConvergence"; default: return "Unknown"; } } /** * @brief Get the string representation of a sign * @param sign sign * @return string representation of the sign */ static std::string toString(int sign) { if (sign < 0) return "Negative"; else if (sign > 0) return "Positive"; else return "Zero"; } /** * @brief Get the string representation of divergence detail * @param detail divergence detail * @return string representation of the divergence detail */ static std::string toString(DivergenceDetail detail) { switch (detail) { case DivergenceDetail::None: return "None"; case DivergenceDetail::Diverging: return "Diverging"; case DivergenceDetail::Oscillating: return "Oscillating"; case DivergenceDetail::Chaotic: return "Chaotic"; case DivergenceDetail::SlowConvergence: return "SlowConvergence"; case DivergenceDetail::TruncatedConvergence: return "TruncatedConvergence"; case DivergenceDetail::ConditionalConvergenceViolation: return "ConditionalConvergenceViolation"; case DivergenceDetail::AlternatingSeriesDivergence: return "AlternatingSeriesDivergence"; default: return "Unknown"; } } /** * @brief Get the string representation of an error cause * @param error error cause * @return string representation of the error cause */ static std::string toString(NumericError error) { switch (error) { case NumericError::None: return "None"; case NumericError::DivideByZero: return "DivideByZero"; case NumericError::InfiniteIndeterminate: return "InfiniteIndeterminate"; case NumericError::ZeroTimesInfinity: return "ZeroTimesInfinity"; case NumericError::NegativeSqrt: return "NegativeSqrt"; case NumericError::ConversionError: return "ConversionError"; case NumericError::ExplicitNaN: return "ExplicitNaN"; case NumericError::Uninitialized: return "Uninitialized"; case NumericError::InvalidBitPattern: return "InvalidBitPattern"; case NumericError::InvalidStringConversion: return "InvalidStringConversion"; case NumericError::OutOfRangeInput: return "OutOfRangeInput"; case NumericError::NaNPropagation: return "NaNPropagation"; case NumericError::IntegerConversionError: return "IntegerConversionError"; case NumericError::FunctionDomainError: return "FunctionDomainError"; case NumericError::InvalidBitOperation: return "InvalidBitOperation"; case NumericError::DivergenceError: return "DivergenceError"; default: return "Unknown"; } } /** * @brief Determine the state from an error cause * @param error error cause * @return corresponding state */ static NumericState errorToState(NumericError error) { switch (error) { case NumericError::None: return NumericState::Normal; case NumericError::DivideByZero: return NumericState::NaN; // For 0/0 // Note: for n/0 the result is PositiveInfinity or NegativeInfinity depending on sign case NumericError::InfiniteIndeterminate: case NumericError::ZeroTimesInfinity: case NumericError::NegativeSqrt: case NumericError::ExplicitNaN: case NumericError::Uninitialized: case NumericError::InvalidBitPattern: case NumericError::InvalidStringConversion: case NumericError::NaNPropagation: case NumericError::FunctionDomainError: case NumericError::InvalidBitOperation: return NumericState::NaN; case NumericError::ConversionError: case NumericError::OutOfRangeInput: case NumericError::IntegerConversionError: return NumericState::Overflow; case NumericError::DivergenceError: return NumericState::Divergent; default: return NumericState::NaN; } } /** * @brief Estimate a default error cause from a state * @param state state * @return inferred error cause * @note This is not a 1-to-1 mapping, so it is not a complete inverse */ static NumericError stateToDefaultError(NumericState state) { switch (state) { case NumericState::Normal: case NumericState::PositiveZero: case NumericState::NegativeZero: return NumericError::None; case NumericState::NaN: return NumericError::ExplicitNaN; case NumericState::PositiveInfinity: case NumericState::NegativeInfinity: case NumericState::ComplexInfinity: // Default is unknown infinity return NumericError::InfiniteIndeterminate; case NumericState::Overflow: return NumericError::OutOfRangeInput; case NumericState::Underflow: return NumericError::OutOfRangeInput; case NumericState::Interrupted: return NumericError::None; case NumericState::PrecisionLimit: return NumericError::None; case NumericState::Subnormal: return NumericError::None; case NumericState::NotConverged: case NumericState::Divergent: case NumericState::Oscillating: case NumericState::SlowConvergence: return NumericError::DivergenceError; default: return NumericError::None; } } /** * @brief Determine whether the state requires an error code * @param state state to test * @return true if an error code is required */ static bool requiresErrorCode(NumericState state) { return state != NumericState::Normal && state != NumericState::PositiveZero && state != NumericState::NegativeZero; } }; /** * @brief Template class that exposes numeric-state properties * * Specialized per numeric type to provide that type's state-management facilities. * Acts as a layer independent of the algebraic-concept system. */ template struct numeric_state_traits { // Not supported by default static constexpr bool is_supported = false; // Dummy methods that are not usable as-is static bool isNormal(const T&) { return true; } static bool isNaN(const T&) { return false; } static bool isInfinite(const T&) { return false; } static bool isNotConverged(const T&) { return false; } static NumericState getState(const T&) { return NumericState::Normal; } static NumericError getError(const T&) { return NumericError::None; } static int getSign(const T&) { return 0; } }; // Helper functions for fundamental types /** * @brief Get the sign * @param value value * @return sign */ template constexpr int getSign(T value) { return value < 0 ? -1 : value > 0 ? 1 : 0; } template constexpr int getSign(T value) { return value < 0 ? -1 : value > 0 ? 1 : 0; } /** * @brief Negate a sign * @param sign sign * @return negated sign */ constexpr int negateSign(int sign) { return -sign; } /** * @brief Determine the sign of a multiplication or division result from two signs * @param a first sign * @param b second sign * @return resulting sign */ constexpr int combineSignsMultiply(int a, int b) { if (a == 0 || b == 0) return 0; return (a == b) ? 1 : -1; } /** * @brief Determine the sign of an addition result from two signs * @param a first sign * @param b second sign * @param absCompare absolute-value comparison (negative: |a|<|b|, 0: |a|=|b|, positive: |a|>|b|) * @return resulting sign */ constexpr int combineSignsAdd(int a, int b, int absCompare) { // When the signs match, the result has the same sign if (a == b) return a; // When the signs differ, the result takes the sign of the larger absolute value if (absCompare < 0) return b; // |a| < |b| else if (absCompare > 0) return a; // |a| > |b| else return 0; // |a| = |b| } // Concepts for state-managed types /** * @brief Concept for types that support state management */ template concept HasNumericState = requires(T a) { { numeric_state_traits::isNormal(a) } -> std::convertible_to; { numeric_state_traits::getState(a) } -> std::convertible_to; { numeric_state_traits::getError(a) } -> std::convertible_to; { numeric_state_traits::getSign(a) } -> std::convertible_to; }; /** * @brief Concept for types that support divergence detail */ template concept HasDivergenceHandling = requires(T a) { { numeric_state_traits::isNotConverged(a) } -> std::convertible_to; { numeric_state_traits::getDivergenceDetail(a) } -> std::convertible_to; }; /** * @brief Concept for types that support overflow detection */ template concept HasOverflowDetection = requires(T a) { { numeric_state_traits::isOverflow(a) } -> std::convertible_to; }; } // namespace sangi #endif // SANGI_NUMERIC_STATE_HPP