// Small numeric helpers shared by the sim formulas. // // The original engine converts floating point to integer by truncation toward zero // (the MSVC float-to-long helper); `Ftol`/`Ftoi64` reproduce that so every rounding site // in this module is explicit about which conversion it performs. #pragma once #include #include namespace sots::sim { // Truncating float -> int32 conversion. Out-of-range input saturates (the original // helper's behaviour there is undefined; saturating keeps our tests deterministic). inline int Ftol(double v) { if (!(v == v)) return 0; // NaN if (v >= 2147483647.0) return 2147483647; if (v <= -2147483648.0) return -2147483647 - 1; return static_cast(v); // C++ static_cast truncates toward zero } // Truncating float -> int64 conversion. inline std::int64_t Ftoi64(double v) { if (!(v == v)) return 0; if (v >= 9223372036854775807.0) return INT64_MAX; if (v <= -9223372036854775808.0) return INT64_MIN; return static_cast(v); } // Round-half-away-from-zero to int (used for the per-system output split). inline int RoundToInt(double v) { return Ftol(std::round(v)); } inline double Clamp01(double v) { return v < 0 ? 0 : (v > 1 ? 1 : v); } template inline T ClampT(T v, T lo, T hi) { return v < lo ? lo : (v > hi ? hi : v); } // Saturating add clamped to +/-2,000,000,000 -- the treasury never overflows. // CONFIDENCE: high. inline int SaturatingAdd(int a, int b) { const std::int64_t s = static_cast(a) + static_cast(b); constexpr std::int64_t kLimit = 2000000000; if (s > kLimit) return static_cast(kLimit); if (s < -kLimit) return static_cast(-kLimit); return static_cast(s); } } // namespace sots::sim