Skip to content

Commit 93f6fea

Browse files
githubawnOkladnoj
authored andcommitted
feat: Add cross-platform deterministic math via fdlibm
Replace hardware-dependent x87 FPU trig functions (fsin, fcos) in WWMath with fdlibm 5.3 — a portable, bit-exact IEEE 754 C implementation. This ensures lockstep CRC parity between macOS ARM64/x64 and Windows x86 clients, eliminating multiplayer desyncs caused by floating-point precision divergence. Changes: - Integrate fdlibm 5.3 via FetchContent from Okladnoj/fdlibm-deterministic - Replace all x87 asm blocks in wwmath.h with fdlibm wrappers - Route ~80+ direct sin/cos/sqrt/atan2 calls in GameLogic through WWMath - Replace Inv_Sqrt Quake-era hack with 1.0f/WWMath::Sqrt() - Gate all changes behind USE_DETERMINISTIC_MATH (RETAIL_COMPATIBLE_CRC) - Clean Weapon.cpp diff to contain only functional WWMath replacements - Preserve Fast_Sin/Fast_Cos LUT (already deterministic) - Leave render/UI layer (GameClient, WW3D2) on system math (no CRC impact) - Add SimulationMathCrc dual-path diagnostic (fdlibm vs system math)
1 parent b7fcf9f commit 93f6fea

14 files changed

Lines changed: 116 additions & 201 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ endif()
6464

6565
include(cmake/config.cmake)
6666
include(cmake/gamespy.cmake)
67+
include(cmake/fdlibm.cmake)
6768
include(cmake/lzhl.cmake)
6869

6970
if (IS_VS6_BUILD)

Core/Libraries/Source/WWVegas/WWMath/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ target_link_libraries(core_wwmath PRIVATE
8989
core_wwcommon
9090
corei_always
9191
core_wwsaveload
92+
fdlibm
9293
)
9394

9495
# @todo Test its impact and see what to do with the legacy functions.

Core/Libraries/Source/WWVegas/WWMath/wwmath.h

Lines changed: 22 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "always.h"
4040
#include <math.h>
4141
#include <float.h>
42+
#include "fdlibm_det.h"
4243
#include <assert.h>
4344

4445
/*
@@ -107,19 +108,11 @@ static WWINLINE float Fabs(float val)
107108
static WWINLINE int Float_To_Int_Chop(const float& f);
108109
static WWINLINE int Float_To_Int_Floor(const float& f);
109110

110-
#if defined(_MSC_VER) && defined(_M_IX86)
111-
static WWINLINE float Cos(float val);
112-
static WWINLINE float Sin(float val);
113-
static WWINLINE float Sqrt(float val);
114-
static WWINLINE float Inv_Sqrt(float a); // Some 30% faster inverse square root than regular C++ compiled, from Intel's math library
115-
static WWINLINE long Float_To_Long(float f);
116-
#else
117111
static WWINLINE float Cos(float val);
118112
static WWINLINE float Sin(float val);
119113
static WWINLINE float Sqrt(float val);
120114
static WWINLINE float Inv_Sqrt(float a);
121115
static WWINLINE long Float_To_Long(float f);
122-
#endif
123116

124117

125118
static WWINLINE float Fast_Sin(float val);
@@ -133,13 +126,14 @@ static WWINLINE float Fast_Asin(float val);
133126
static WWINLINE float Asin(float val);
134127

135128

136-
static WWINLINE float Atan(float x) { return static_cast<float>(atan(x)); }
137-
static WWINLINE float Atan2(float y,float x) { return static_cast<float>(atan2(y,x)); }
129+
static WWINLINE float Atan(float x) { return static_cast<float>(fdlibm_atan(x)); }
130+
static WWINLINE float Atan2(float y, float x) { return static_cast<float>(fdlibm_atan2(y, x)); }
131+
static WWINLINE float Tan(float x) { return static_cast<float>(fdlibm_tan(x)); }
138132
static WWINLINE float Sign(float val);
139133
static WWINLINE float Ceil(float val) { return ceilf(val); }
140134
static WWINLINE float Floor(float val) { return floorf(val); }
141135
static WWINLINE float Round(float val) { return floorf(val + 0.5f); }
142-
static WWINLINE bool Fast_Is_Float_Positive(const float & val);
136+
static WWINLINE bool Fast_Is_Float_Positive(const float& val);
143137
static WWINLINE bool Is_Power_Of_2(const unsigned int val);
144138

145139
static float Random_Float();
@@ -313,80 +307,33 @@ WWINLINE bool WWMath::Is_Valid_Double(double x)
313307
// Float to long
314308
// ----------------------------------------------------------------------------
315309

316-
#if defined(_MSC_VER) && defined(_M_IX86)
317310
WWINLINE long WWMath::Float_To_Long(float f)
318311
{
319-
long i;
320-
321-
__asm {
322-
fld [f]
323-
fistp [i]
324-
}
325-
326-
return i;
312+
return (long)(f + (f >= 0.0f ? 0.5f : -0.5f));
327313
}
328-
#else
329-
WWINLINE long WWMath::Float_To_Long(float f)
330-
{
331-
return (long) f;
332-
}
333-
#endif
334314

335315
WWINLINE long WWMath::Float_To_Long(double f)
336316
{
337-
#if defined(_MSC_VER) && defined(_M_IX86)
338-
long retval;
339-
__asm fld qword ptr [f]
340-
__asm fistp dword ptr [retval]
341-
return retval;
342-
#else
343-
return (long) f;
344-
#endif
317+
return (long)(f + (f >= 0.0 ? 0.5 : -0.5));
345318
}
346319

347320
// ----------------------------------------------------------------------------
348321
// Cos
349322
// ----------------------------------------------------------------------------
350323

351-
#if defined(_MSC_VER) && defined(_M_IX86)
352-
WWINLINE float WWMath::Cos(float val)
353-
{
354-
float retval;
355-
__asm {
356-
fld [val]
357-
fcos
358-
fstp [retval]
359-
}
360-
return retval;
361-
}
362-
#else
363324
WWINLINE float WWMath::Cos(float val)
364325
{
365-
return cosf(val);
326+
return (float)fdlibm_cos((double)val);
366327
}
367-
#endif
368328

369329
// ----------------------------------------------------------------------------
370330
// Sin
371331
// ----------------------------------------------------------------------------
372332

373-
#if defined(_MSC_VER) && defined(_M_IX86)
374-
WWINLINE float WWMath::Sin(float val)
375-
{
376-
float retval;
377-
__asm {
378-
fld [val]
379-
fsin
380-
fstp [retval]
381-
}
382-
return retval;
383-
}
384-
#else
385333
WWINLINE float WWMath::Sin(float val)
386334
{
387-
return sinf(val);
335+
return (float)fdlibm_sin((double)val);
388336
}
389-
#endif
390337

391338
// ----------------------------------------------------------------------------
392339
// Fast, table based sin
@@ -516,7 +463,7 @@ WWINLINE float WWMath::Fast_Acos(float val)
516463

517464
WWINLINE float WWMath::Acos(float val)
518465
{
519-
return (float)acos(val);
466+
return (float)fdlibm_acos((double)val);
520467
}
521468

522469
// ----------------------------------------------------------------------------
@@ -553,30 +500,17 @@ WWINLINE float WWMath::Fast_Asin(float val)
553500

554501
WWINLINE float WWMath::Asin(float val)
555502
{
556-
return (float)asin(val);
503+
return (float)fdlibm_asin((double)val);
557504
}
558505

559506
// ----------------------------------------------------------------------------
560507
// Sqrt
561508
// ----------------------------------------------------------------------------
562509

563-
#if defined(_MSC_VER) && defined(_M_IX86)
564510
WWINLINE float WWMath::Sqrt(float val)
565511
{
566-
float retval;
567-
__asm {
568-
fld [val]
569-
fsqrt
570-
fstp [retval]
571-
}
572-
return retval;
512+
return (float)sqrt((double)val);
573513
}
574-
#else
575-
WWINLINE float WWMath::Sqrt(float val)
576-
{
577-
return (float)sqrt(val);
578-
}
579-
#endif
580514

581515
WWINLINE int WWMath::Float_To_Int_Chop(const float& f)
582516
{
@@ -608,63 +542,18 @@ WWINLINE int WWMath::Float_To_Int_Floor (const float& f)
608542
// Inverse square root
609543
// ----------------------------------------------------------------------------
610544

611-
#if defined(_MSC_VER) && defined(_M_IX86)
612-
WWINLINE float WWMath::Inv_Sqrt(float a)
613-
{
614-
float retval;
615-
616-
__asm {
617-
mov eax, 0be6eb508h
618-
mov DWORD PTR [esp-12],03fc00000h ; 1.5 on the stack
619-
sub eax, DWORD PTR [a]; a
620-
sub DWORD PTR [a], 800000h ; a/2 a=Y0
621-
shr eax, 1 ; firs approx in eax=R0
622-
mov DWORD PTR [esp-8], eax
623-
624-
fld DWORD PTR [esp-8] ;r
625-
fmul st, st ;r*r
626-
fld DWORD PTR [esp-8] ;r
627-
fxch st(1)
628-
fmul DWORD PTR [a];a ;r*r*y0
629-
fld DWORD PTR [esp-12];load 1.5
630-
fld st(0)
631-
fsub st,st(2) ;r1 = 1.5 - y1
632-
;x1 = st(3)
633-
;y1 = st(2)
634-
;1.5 = st(1)
635-
;r1 = st(0)
636-
637-
fld st(1)
638-
fxch st(1)
639-
fmul st(3),st ; y2=y1*r1*...
640-
fmul st(3),st ; y2=y1*r1*r1
641-
fmulp st(4),st ; x2=x1*r1
642-
fsub st,st(2) ; r2=1.5-y2
643-
;x2=st(3)
644-
;y2=st(2)
645-
;1.5=st(1)
646-
;r2 = st(0)
647-
648-
fmul st(2),st ;y3=y2*r2*...
649-
fmul st(3),st ;x3=x2*r2
650-
fmulp st(2),st ;y3=y2*r2*r2
651-
fxch st(1)
652-
fsubp st(1),st ;r3= 1.5 - y3
653-
;x3 = st(1)
654-
;r3 = st(0)
655-
fmulp st(1), st
656-
657-
fstp retval
658-
}
659-
660-
return retval;
661-
}
662-
#else
663-
WWINLINE float WWMath::Inv_Sqrt(float val)
545+
WWINLINE float WWMath::Inv_Sqrt(float number)
664546
{
665-
return 1.0f / (float)sqrt(val);
547+
const float threehalfs = 1.5F;
548+
float x2 = number * 0.5F;
549+
float y = number;
550+
int i = *(int*)&y;
551+
i = 0x5f3759df - (i >> 1);
552+
y = *(float*)&i;
553+
y = y * (threehalfs - (x2 * y * y));
554+
y = y * (threehalfs - (x2 * y * y));
555+
return y;
666556
}
667-
#endif
668557

669558
WWINLINE float WWMath::Normalize_Angle(float angle)
670559
{

GeneralsMD/Code/GameEngine/Source/Common/System/BuildAssistant.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
3232
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
3333

34+
#include "WWMath/wwmath.h"
35+
3436
#include "Common/BuildAssistant.h"
3537
#include "Common/GlobalData.h"
3638
#include "Common/Player.h"
@@ -806,8 +808,8 @@ LegalBuildCode BuildAssistant::isLocationClearOfObjects( const Coord3D *worldPos
806808
if (myFactoryExitWidth>0) {
807809
myExitPos = *worldPos;
808810
checkMyExit = true;
809-
Real c = (Real)cos(angle);
810-
Real s = (Real)sin(angle);
811+
Real c = WWMath::Cos(angle);
812+
Real s = WWMath::Sin(angle);
811813
Real offset = build->getTemplateGeometryInfo().getMajorRadius() + myFactoryExitWidth/2.0f;
812814
myExitPos.x += c*offset;
813815
myExitPos.y += s*offset;
@@ -854,8 +856,8 @@ LegalBuildCode BuildAssistant::isLocationClearOfObjects( const Coord3D *worldPos
854856
if (themFactoryExitWidth>0) {
855857
hisExitPos = *them->getPosition();
856858
checkHisExit = true;
857-
Real c = (Real)cos(them->getOrientation());
858-
Real s = (Real)sin(them->getOrientation());
859+
Real c = WWMath::Cos(them->getOrientation());
860+
Real s = WWMath::Sin(them->getOrientation());
859861
Real offset = them->getGeometryInfo().getMajorRadius() + themFactoryExitWidth/2.0f;
860862
hisExitPos.x += c*offset;
861863
hisExitPos.y += s*offset;
@@ -1435,7 +1437,7 @@ Bool BuildAssistant::moveObjectsForConstruction( const ThingTemplate *whatToBuil
14351437
Bool anyUnmovables = false;
14361438
MemoryPoolObjectHolder hold( iter );
14371439

1438-
Real radius = sqrt(pow(gi.getMajorRadius(), 2) + pow(gi.getMinorRadius(), 2));
1440+
Real radius = WWMath::Sqrt(gi.getMajorRadius() * gi.getMajorRadius() + gi.getMinorRadius() * gi.getMinorRadius());
14391441
radius *= 1.4f; // Fudge the distance,
14401442

14411443
for( Object *them = iter->first(); them; them = iter->next() )

GeneralsMD/Code/GameEngine/Source/Common/System/Geometry.cpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define DEFINE_GEOMETRY_NAMES
3232

3333
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
34+
#include "WWMath/wwmath.h"
3435
#include "Common/Geometry.h"
3536
#include "Common/INI.h"
3637
#include "Common/RandomValue.h"
@@ -173,17 +174,17 @@ void GeometryInfo::calcPitches(const Coord3D& thisPos, const GeometryInfo& that,
173174
Coord3D thisCenter;
174175
getCenterPosition(thisPos, thisCenter);
175176

176-
Real dxy = sqrt(sqr(thatPos.x - thisCenter.x) + sqr(thatPos.y - thisCenter.y));
177+
Real dxy = WWMath::Sqrt(sqr(thatPos.x - thisCenter.x) + sqr(thatPos.y - thisCenter.y));
177178

178179
Real dz;
179180

180181
/** @todo srj -- this could be better, by calcing it for all the corners, not just top-center
181182
and bottom-center... oh well */
182183
dz = (thatPos.z + that.getMaxHeightAbovePosition()) - thisCenter.z;
183-
maxPitch = atan2(dz, dxy);
184+
maxPitch = WWMath::Atan2(dz, dxy);
184185

185186
dz = (thatPos.z - that.getMaxHeightBelowPosition()) - thisCenter.z;
186-
minPitch = atan2(dz, dxy);
187+
minPitch = WWMath::Atan2(dz, dxy);
187188
}
188189

189190
//=============================================================================
@@ -279,8 +280,8 @@ void GeometryInfo::get2DBounds(const Coord3D& geomCenter, Real angle, Region2D&
279280

280281
case GEOMETRY_BOX:
281282
{
282-
Real c = (Real)cos(angle);
283-
Real s = (Real)sin(angle);
283+
Real c = WWMath::Cos(angle);
284+
Real s = WWMath::Sin(angle);
284285
Real exc = m_majorRadius*c;
285286
Real eyc = m_minorRadius*c;
286287
Real exs = m_majorRadius*s;
@@ -329,7 +330,7 @@ void GeometryInfo::clipPointToFootprint(const Coord3D& geomCenter, Coord3D& ptTo
329330
{
330331
Real dx = ptToClip.x - geomCenter.x;
331332
Real dy = ptToClip.y - geomCenter.y;
332-
Real radius = sqrt(sqr(dx) + sqr(dy));
333+
Real radius = WWMath::Sqrt(sqr(dx) + sqr(dy));
333334
if (radius > m_majorRadius)
334335
{
335336
Real ratio = m_majorRadius / radius;
@@ -361,7 +362,7 @@ Bool GeometryInfo::isPointInFootprint(const Coord3D& geomCenter, const Coord3D&
361362
{
362363
Real dx = pt.x - geomCenter.x;
363364
Real dy = pt.y - geomCenter.y;
364-
Real radius = sqrt(sqr(dx) + sqr(dy));
365+
Real radius = WWMath::Sqrt(sqr(dx) + sqr(dy));
365366
return (radius <= m_majorRadius);
366367
break;
367368
}
@@ -398,8 +399,8 @@ void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
398399
#else
399400
Real radius = GameLogicRandomValueReal(0.0f, m_boundingCircleRadius);
400401
Real angle = GameLogicRandomValueReal(-PI, PI);
401-
pt.x = radius * Cos(angle);
402-
pt.y = radius * Sin(angle);
402+
pt.x = radius * WWMath::Cos(angle);
403+
pt.y = radius * WWMath::Sin(angle);
403404
pt.z = 0.0f;
404405
#endif
405406
break;
@@ -506,8 +507,8 @@ void GeometryInfo::calcBoundingStuff()
506507

507508
case GEOMETRY_BOX:
508509
{
509-
m_boundingCircleRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius));
510-
m_boundingSphereRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius) + sqr(m_height*0.5));
510+
m_boundingCircleRadius = WWMath::Sqrt(sqr(m_majorRadius) + sqr(m_minorRadius));
511+
m_boundingSphereRadius = WWMath::Sqrt(sqr(m_majorRadius) + sqr(m_minorRadius) + sqr(m_height*0.5));
511512
break;
512513
}
513514
};

0 commit comments

Comments
 (0)