diff --git a/CMakeLists.txt b/CMakeLists.txt
index 65385da8e..9a4452c73 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -962,8 +962,8 @@ if(SDL_LIBC)
_stricmp _strnicmp sscanf
acos acosf asin asinf atan atanf atan2 atan2f ceil ceilf
copysign copysignf cos cosf exp expf fabs fabsf floor floorf fmod fmodf
- log logf log10 log10f lround lroundf pow powf round roundf scalbn scalbnf
- sin sinf sqrt sqrtf tan tanf trunc truncf)
+ log logf log10 log10f lround lroundf modf modff pow powf round roundf
+ scalbn scalbnf sin sinf sqrt sqrtf tan tanf trunc truncf)
string(TOUPPER ${_FN} _UPPER)
set(HAVE_${_UPPER} 1)
endforeach()
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index 381213fc0..d6df2314d 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -666,6 +666,7 @@
+
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index 40c9fd6a7..4db42dd26 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -976,6 +976,9 @@
libm
+
+ libm
+
libm
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 798033270..756ea5934 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -546,6 +546,7 @@
+
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index eb48c1e2d..acb558a16 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -964,6 +964,9 @@
libm
+
+ libm
+
libm
diff --git a/WhatsNew.txt b/WhatsNew.txt
index 10625d97a..6733ad8d9 100644
--- a/WhatsNew.txt
+++ b/WhatsNew.txt
@@ -19,3 +19,4 @@ General:
* Added SDL_GetTicksNS() to return the number of nanoseconds since the SDL library initialized
* Added SDL_DelayNS() to specify a delay in nanoseconds, to the highest precision the system will support
* The timestamp member of the SDL_Event structure is now in nanoseconds, filled in with the time the event was generated, or the time it was queued if that's not available
+* Added SDL_modf() and SDL_modff() to separate the whole and fractional portions of a floating point number
diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h
index 75df76aae..75fc0b5eb 100644
--- a/include/SDL3/SDL_stdinc.h
+++ b/include/SDL3/SDL_stdinc.h
@@ -580,6 +580,8 @@ extern DECLSPEC double SDLCALL SDL_log(double x);
extern DECLSPEC float SDLCALL SDL_logf(float x);
extern DECLSPEC double SDLCALL SDL_log10(double x);
extern DECLSPEC float SDLCALL SDL_log10f(float x);
+extern DECLSPEC double SDLCALL SDL_modf(double x, double *y);
+extern DECLSPEC float SDLCALL SDL_modff(float x, float *y);
extern DECLSPEC double SDLCALL SDL_pow(double x, double y);
extern DECLSPEC float SDLCALL SDL_powf(float x, float y);
extern DECLSPEC double SDLCALL SDL_round(double x);
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index 96eb44e51..87e9c65d7 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -171,6 +171,8 @@
#cmakedefine HAVE_LOG10F 1
#cmakedefine HAVE_LROUND 1
#cmakedefine HAVE_LROUNDF 1
+#cmakedefine HAVE_MODF 1
+#cmakedefine HAVE_MODFF 1
#cmakedefine HAVE_POW 1
#cmakedefine HAVE_POWF 1
#cmakedefine HAVE_ROUND 1
diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h
index dd5ea3efd..4572f9aa9 100644
--- a/include/build_config/SDL_build_config_android.h
+++ b/include/build_config/SDL_build_config_android.h
@@ -120,6 +120,7 @@
#define HAVE_LOG10F 1
#define HAVE_LROUND 1
#define HAVE_LROUNDF 1
+#define HAVE_MODF 1
#define HAVE_POW 1
#define HAVE_POWF 1
#define HAVE_ROUND 1
diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h
index 0bb63efc3..d4f14c28f 100644
--- a/include/build_config/SDL_build_config_ios.h
+++ b/include/build_config/SDL_build_config_ios.h
@@ -114,6 +114,7 @@
#define HAVE_LOG10F 1
#define HAVE_LROUND 1
#define HAVE_LROUNDF 1
+#define HAVE_MODF 1
#define HAVE_POW 1
#define HAVE_POWF 1
#define HAVE_ROUND 1
diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h
index 576c4dc5b..f2878db25 100644
--- a/include/build_config/SDL_build_config_macos.h
+++ b/include/build_config/SDL_build_config_macos.h
@@ -116,6 +116,7 @@
#define HAVE_LOG10F 1
#define HAVE_LROUND 1
#define HAVE_LROUNDF 1
+#define HAVE_MODF 1
#define HAVE_POW 1
#define HAVE_POWF 1
#define HAVE_ROUND 1
diff --git a/include/build_config/SDL_build_config_winrt.h b/include/build_config/SDL_build_config_winrt.h
index 4256cea42..01bff80e1 100644
--- a/include/build_config/SDL_build_config_winrt.h
+++ b/include/build_config/SDL_build_config_winrt.h
@@ -134,6 +134,7 @@
#define HAVE_LOG10F 1
#define HAVE_LROUND 1
#define HAVE_LROUNDF 1
+#define HAVE_MODF 1
#define HAVE_POW 1
#define HAVE_POWF 1
#define HAVE_ROUND 1
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 2876dea5d..540c00ebf 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -859,6 +859,8 @@ SDL3_0.0.0 {
SDL_wcsncasecmp;
SDL_wcsncmp;
SDL_wcsstr;
+ SDL_modf;
+ SDL_modff;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 1228b3ba7..2bc07ba4f 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -887,3 +887,5 @@
#define SDL_wcsstr SDL_wcsstr_REAL
/* New API symbols are added at the end */
+#define SDL_modf SDL_modf_REAL
+#define SDL_modff SDL_modff_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index f4cf4b731..62bf7dd39 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -932,3 +932,5 @@ SDL_DYNAPI_PROC(int,SDL_wcsncmp,(const wchar_t *a, const wchar_t *b, size_t c),(
SDL_DYNAPI_PROC(wchar_t*,SDL_wcsstr,(const wchar_t *a, const wchar_t *b),(a,b),return)
/* New API symbols are added at the end */
+SDL_DYNAPI_PROC(double,SDL_modf,(double a, double *b),(a,b),return)
+SDL_DYNAPI_PROC(float,SDL_modff,(float a, float *b),(a,b),return)
diff --git a/src/libm/math_libm.h b/src/libm/math_libm.h
index f88c73bf3..5ed5f21a3 100644
--- a/src/libm/math_libm.h
+++ b/src/libm/math_libm.h
@@ -27,16 +27,17 @@
/* Math routines from uClibc: http://www.uclibc.org */
double SDL_uclibc_atan(double x);
-double SDL_uclibc_atan2(double y, double x);
-double SDL_uclibc_copysign(double x, double y);
-double SDL_uclibc_cos(double x);
+double SDL_uclibc_atan2(double y, double x);
+double SDL_uclibc_copysign(double x, double y);
+double SDL_uclibc_cos(double x);
double SDL_uclibc_exp(double x);
-double SDL_uclibc_fabs(double x);
+double SDL_uclibc_fabs(double x);
double SDL_uclibc_floor(double x);
double SDL_uclibc_fmod(double x, double y);
double SDL_uclibc_log(double x);
double SDL_uclibc_log10(double x);
-double SDL_uclibc_pow(double x, double y);
+double SDL_uclibc_modf(double x, double *y);
+double SDL_uclibc_pow(double x, double y);
double SDL_uclibc_scalbn(double x, int n);
double SDL_uclibc_sin(double x);
double SDL_uclibc_sqrt(double x);
diff --git a/src/libm/math_private.h b/src/libm/math_private.h
index 8a468bf47..ba5d83468 100644
--- a/src/libm/math_private.h
+++ b/src/libm/math_private.h
@@ -40,6 +40,7 @@ typedef unsigned int u_int32_t;
#define __ieee754_fmod SDL_uclibc_fmod
#define __ieee754_log SDL_uclibc_log
#define __ieee754_log10 SDL_uclibc_log10
+#define modf SDL_uclibc_modf
#define __ieee754_pow SDL_uclibc_pow
#define scalbln SDL_uclibc_scalbln
#define scalbn SDL_uclibc_scalbn
diff --git a/src/libm/s_modf.c b/src/libm/s_modf.c
new file mode 100644
index 000000000..55f83ba8d
--- /dev/null
+++ b/src/libm/s_modf.c
@@ -0,0 +1,68 @@
+#include "SDL_internal.h"
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+/*
+ * modf(double x, double *iptr)
+ * return fraction part of x, and return x's integral part in *iptr.
+ * Method:
+ * Bit twiddling.
+ *
+ * Exception:
+ * No exception.
+ */
+
+#include "math_libm.h"
+#include "math_private.h"
+
+static const double one = 1.0;
+
+double modf(double x, double *iptr)
+{
+ int32_t i0,i1,_j0;
+ u_int32_t i;
+ EXTRACT_WORDS(i0,i1,x);
+ _j0 = ((i0>>20)&0x7ff)-0x3ff; /* exponent of x */
+ if(_j0<20) { /* integer part in high x */
+ if(_j0<0) { /* |x|<1 */
+ INSERT_WORDS(*iptr,i0&0x80000000,0); /* *iptr = +-0 */
+ return x;
+ } else {
+ i = (0x000fffff)>>_j0;
+ if(((i0&i)|i1)==0) { /* x is integral */
+ *iptr = x;
+ INSERT_WORDS(x,i0&0x80000000,0); /* return +-0 */
+ return x;
+ } else {
+ INSERT_WORDS(*iptr,i0&(~i),0);
+ return x - *iptr;
+ }
+ }
+ } else if (_j0>51) { /* no fraction part */
+ *iptr = x*one;
+ /* We must handle NaNs separately. */
+ if (_j0 == 0x400 && ((i0 & 0xfffff) | i1))
+ return x*one;
+ INSERT_WORDS(x,i0&0x80000000,0); /* return +-0 */
+ return x;
+ } else { /* fraction part in low x */
+ i = ((u_int32_t)(0xffffffff))>>(_j0-20);
+ if((i1&i)==0) { /* x is integral */
+ *iptr = x;
+ INSERT_WORDS(x,i0&0x80000000,0); /* return +-0 */
+ return x;
+ } else {
+ INSERT_WORDS(*iptr,i0,i1&(~i));
+ return x - *iptr;
+ }
+ }
+}
+libm_hidden_def(modf)
diff --git a/src/stdlib/SDL_stdlib.c b/src/stdlib/SDL_stdlib.c
index 7663423fa..8c567cf5b 100644
--- a/src/stdlib/SDL_stdlib.c
+++ b/src/stdlib/SDL_stdlib.c
@@ -322,6 +322,28 @@ float SDL_log10f(float x)
#endif
}
+double
+SDL_modf(double x, double *y)
+{
+#if defined(HAVE_MODF)
+ return modf(x, y);
+#else
+ return SDL_uclibc_modf(x, y);
+#endif
+}
+
+float SDL_modff(float x, float *y)
+{
+#if defined(HAVE_MODFF)
+ return modff(x, y);
+#else
+ double double_result, double_y;
+ double_result = SDL_modf((double)x, &double_y);
+ *y = (float)double_y;
+ return (float)double_result;
+#endif
+}
+
double
SDL_pow(double x, double y)
{
diff --git a/test/testautomation_math.c b/test/testautomation_math.c
index 7103854e0..4bdc2f7e9 100644
--- a/test/testautomation_math.c
+++ b/test/testautomation_math.c
@@ -1272,6 +1272,24 @@ log10_regularCases(void *args)
return helper_dtod_inexact("Log10", SDL_log10, regular_cases, SDL_arraysize(regular_cases));
}
+/* SDL_modf tests functions */
+
+static int
+modf_baseCases(void *args)
+{
+ double fractional, integral;
+
+ fractional = SDL_modf(1.25, &integral);
+ SDLTest_AssertCheck(integral == 1.0,
+ "modf(%f), expected integral %f, got %f",
+ 1.25, 1.0, integral);
+ SDLTest_AssertCheck(fractional == 0.25,
+ "modf(%f), expected fractional %f, got %f",
+ 1.25, 0.25, fractional);
+
+ return TEST_COMPLETED;
+}
+
/* SDL_pow tests functions */
/* Tests with positive and negative infinities as exponents */
@@ -3004,6 +3022,13 @@ static const SDLTest_TestCaseReference log10TestRegular = {
"Checks a set of regular values", TEST_ENABLED
};
+/* SDL_modf test cases */
+
+static const SDLTest_TestCaseReference modfTestBase = {
+ (SDLTest_TestCaseFp)modf_baseCases, "modf_baseCases",
+ "Checks the base cases", TEST_ENABLED
+};
+
/* SDL_pow test cases */
static const SDLTest_TestCaseReference powTestExpInf1 = {
@@ -3315,6 +3340,8 @@ static const SDLTest_TestCaseReference *mathTests[] = {
&log10TestLimit, &log10TestNan,
&log10TestBase, &log10TestRegular,
+ &modfTestBase,
+
&powTestExpInf1, &powTestExpInf2, &powTestExpInf3,
&powTestBaseInf1, &powTestBaseInf2,
&powTestNan1, &powTestNan2, &powTestNan3, &powTestNan4,