From 7f1e78df0d399ed69a6376e03f6fa62bc9eb4381 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:20:50 +0100 Subject: [PATCH] fix: Replace fast_float_trunc, fast_float_floor, fast_float_ceil with standard or gamemath equivalents for all configurations except retail compatible Zero Hour fast_float_floor and fast_float_ceil subtract or add almost 1 before truncating, which only gives the right result if the FPU rounds toward zero. The game sets the FPU to round to nearest, so floor returns n-1 for negative whole numbers, ceil returns n+1 for positive whole numbers, and values closer to zero than about 3e-8 return 0. fast_float_trunc also loses bits that are not fractional for magnitudes of 2^32 and larger. REAL_TO_INT_FLOOR and REAL_TO_INT_CEIL, which about 270 places use, carried these errors into all configurations except a retail compatible Generals. A retail compatible Zero Hour keeps the fast functions, because its replays depend on their results. The functions are now only available there and document their errors. All other configurations use gm_floorf and gm_ceilf with deterministic math, or floorf and ceilf otherwise. For example REAL_TO_INT_CEIL(2.0f) is now 2 instead of 3 there. A retail compatible Generals keeps floorf and ceilf as before. gmath.h is now included and detected with angle brackets, because it is found through the include path and not next to the including file. --- .../Source/Lib/Include/Lib/BaseDefines.h | 2 +- .../Source/Lib/Include/Lib/BaseFunctions.h | 33 +++++++++++++++---- Core/Libraries/Source/Lib/Trig.cpp | 2 +- Core/Libraries/Source/WWVegas/WWMath/wwmath.h | 2 +- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Core/Libraries/Source/Lib/Include/Lib/BaseDefines.h b/Core/Libraries/Source/Lib/Include/Lib/BaseDefines.h index b52ed71f4e1..c87defd9448 100644 --- a/Core/Libraries/Source/Lib/Include/Lib/BaseDefines.h +++ b/Core/Libraries/Source/Lib/Include/Lib/BaseDefines.h @@ -31,7 +31,7 @@ #endif #if defined(__has_include) -#if __has_include("gmath.h") +#if __has_include() #define HAS_GAMEMATH (1) #endif #endif diff --git a/Core/Libraries/Source/Lib/Include/Lib/BaseFunctions.h b/Core/Libraries/Source/Lib/Include/Lib/BaseFunctions.h index c481fb73c0d..4ca2d3ac459 100644 --- a/Core/Libraries/Source/Lib/Include/Lib/BaseFunctions.h +++ b/Core/Libraries/Source/Lib/Include/Lib/BaseFunctions.h @@ -21,6 +21,10 @@ #include #include "BaseDefines.h" +#if USE_DETERMINISTIC_MATH +#include +#endif + //----------------------------------------------------------------------------- inline Real rad2deg(Real rad) { return rad * (180/PI); } inline Real deg2rad(Real rad) { return rad * (PI/180); } @@ -46,8 +50,11 @@ __forceinline long fast_float2long_round(float f) return i; } +#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC // super fast float trunc routine, works always (independent of any FPU modes) // code courtesy of Martin Hoffesommer (grin) +// TheSuperHackers @info The shift count wraps at 32, so magnitudes of 2^32 and larger lose +// bits that are not fractional, and NaN becomes infinity. Retail Zero Hour depends on these results. __forceinline float fast_float_trunc(float f) { #if defined(_MSC_VER) && _MSC_VER < 1300 @@ -75,6 +82,10 @@ __forceinline float fast_float_trunc(float f) } // same here, fast floor function +// TheSuperHackers @info Subtracting almost 1 only floors correctly if the FPU rounds toward zero, but the game +// rounds to nearest (see setFPMode). So negative whole numbers above -2^24 return n-1, for example -2 returns -3. +// Negative values closer to zero than about 3e-8 also return 0 instead of -1. +// Retail Zero Hour depends on these results. __forceinline float fast_float_floor(float f) { static unsigned almost1=(126<<23)|0x7fffff; @@ -84,6 +95,10 @@ __forceinline float fast_float_floor(float f) } // same here, fast ceil function +// TheSuperHackers @info Adding almost 1 only ceils correctly if the FPU rounds toward zero, but the game +// rounds to nearest (see setFPMode). So positive whole numbers below 2^24 return n+1, for example 2 returns 3. +// Positive values closer to zero than about 3e-8 also return 0 instead of 1. +// Retail Zero Hour depends on these results. __forceinline float fast_float_ceil(float f) { static unsigned almost1=(126<<23)|0x7fffff; @@ -91,14 +106,20 @@ __forceinline float fast_float_ceil(float f) f+=std::bit_cast(almost1); return fast_float_trunc(f); } +#endif // RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC -// once we've ceiled/floored, trunc and round are identical, and currently, round is faster... (srj) -#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC -#define REAL_TO_INT_CEIL(x) (fast_float2long_round(ceilf(x))) -#define REAL_TO_INT_FLOOR(x) (fast_float2long_round(floorf(x))) +#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC + // once we've ceiled/floored, trunc and round are identical, and currently, round is faster... (srj) + #define REAL_TO_INT_CEIL(x) (fast_float2long_round(fast_float_ceil(x))) + #define REAL_TO_INT_FLOOR(x) (fast_float2long_round(fast_float_floor(x))) #else -#define REAL_TO_INT_CEIL(x) (fast_float2long_round(fast_float_ceil(x))) -#define REAL_TO_INT_FLOOR(x) (fast_float2long_round(fast_float_floor(x))) + #if USE_DETERMINISTIC_MATH + #define REAL_TO_INT_CEIL(x) (fast_float2long_round(gm_ceilf(x))) + #define REAL_TO_INT_FLOOR(x) (fast_float2long_round(gm_floorf(x))) + #else + #define REAL_TO_INT_CEIL(x) (fast_float2long_round(ceilf(x))) + #define REAL_TO_INT_FLOOR(x) (fast_float2long_round(floorf(x))) + #endif #endif //------------------------------------------------------------------------------------------------- diff --git a/Core/Libraries/Source/Lib/Trig.cpp b/Core/Libraries/Source/Lib/Trig.cpp index 813907cdacd..b1c1492848b 100644 --- a/Core/Libraries/Source/Lib/Trig.cpp +++ b/Core/Libraries/Source/Lib/Trig.cpp @@ -33,7 +33,7 @@ #include "Lib/trig.h" #if USE_DETERMINISTIC_MATH -#include "gmath.h" +#include #endif Real Sin(Real x) diff --git a/Core/Libraries/Source/WWVegas/WWMath/wwmath.h b/Core/Libraries/Source/WWVegas/WWMath/wwmath.h index ff5b3649bd9..d27e4525315 100644 --- a/Core/Libraries/Source/WWVegas/WWMath/wwmath.h +++ b/Core/Libraries/Source/WWVegas/WWMath/wwmath.h @@ -44,7 +44,7 @@ #include "Lib/BaseDefines.h" #if USE_DETERMINISTIC_MATH -#include "gmath.h" +#include #endif /*