xref: /reactos/sdk/lib/crt/math/libm_sse2/remainderf.c (revision 9e8ed3f8)
14afb647cSTimo Kreuzer 
24afb647cSTimo Kreuzer /*******************************************************************************
34afb647cSTimo Kreuzer MIT License
44afb647cSTimo Kreuzer -----------
54afb647cSTimo Kreuzer 
64afb647cSTimo Kreuzer Copyright (c) 2002-2019 Advanced Micro Devices, Inc.
74afb647cSTimo Kreuzer 
84afb647cSTimo Kreuzer Permission is hereby granted, free of charge, to any person obtaining a copy
94afb647cSTimo Kreuzer of this Software and associated documentaon files (the "Software"), to deal
104afb647cSTimo Kreuzer in the Software without restriction, including without limitation the rights
114afb647cSTimo Kreuzer to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
124afb647cSTimo Kreuzer copies of the Software, and to permit persons to whom the Software is
134afb647cSTimo Kreuzer furnished to do so, subject to the following conditions:
144afb647cSTimo Kreuzer 
154afb647cSTimo Kreuzer The above copyright notice and this permission notice shall be included in
164afb647cSTimo Kreuzer all copies or substantial portions of the Software.
174afb647cSTimo Kreuzer 
184afb647cSTimo Kreuzer THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
194afb647cSTimo Kreuzer IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
204afb647cSTimo Kreuzer FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
214afb647cSTimo Kreuzer AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
224afb647cSTimo Kreuzer LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
234afb647cSTimo Kreuzer OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
244afb647cSTimo Kreuzer THE SOFTWARE.
254afb647cSTimo Kreuzer *******************************************************************************/
264afb647cSTimo Kreuzer 
274afb647cSTimo Kreuzer #include "libm.h"
284afb647cSTimo Kreuzer #include "libm_util.h"
294afb647cSTimo Kreuzer 
304afb647cSTimo Kreuzer #define USE_NANF_WITH_FLAGS
314afb647cSTimo Kreuzer #define USE_SCALEDOUBLE_1
324afb647cSTimo Kreuzer #define USE_GET_FPSW_INLINE
334afb647cSTimo Kreuzer #define USE_SET_FPSW_INLINE
344afb647cSTimo Kreuzer #define USE_HANDLE_ERRORF
354afb647cSTimo Kreuzer #include "libm_inlines.h"
364afb647cSTimo Kreuzer #undef USE_NANF_WITH_FLAGS
374afb647cSTimo Kreuzer #undef USE_SCALEDOUBLE_1
384afb647cSTimo Kreuzer #undef USE_GET_FPSW_INLINE
394afb647cSTimo Kreuzer #undef USE_SET_FPSW_INLINE
404afb647cSTimo Kreuzer #undef USE_HANDLE_ERRORF
414afb647cSTimo Kreuzer 
424afb647cSTimo Kreuzer #if !defined(_CRTBLD_C9X)
434afb647cSTimo Kreuzer #define _CRTBLD_C9X
444afb647cSTimo Kreuzer #endif
454afb647cSTimo Kreuzer 
464afb647cSTimo Kreuzer #include "libm_errno.h"
474afb647cSTimo Kreuzer 
484afb647cSTimo Kreuzer // Disable "C4163: not available as intrinsic function" warning that older
494afb647cSTimo Kreuzer // compilers may issue here.
504afb647cSTimo Kreuzer #pragma warning(disable:4163)
514afb647cSTimo Kreuzer #pragma function(remainderf,fmodf)
524afb647cSTimo Kreuzer 
534afb647cSTimo Kreuzer 
544afb647cSTimo Kreuzer #undef _FUNCNAME
554afb647cSTimo Kreuzer #if defined(COMPILING_FMOD)
fmodf(float x,float y)564afb647cSTimo Kreuzer float fmodf(float x, float y)
574afb647cSTimo Kreuzer #define _FUNCNAME "fmodf"
584afb647cSTimo Kreuzer #define _OPERATION OP_FMOD
594afb647cSTimo Kreuzer #else
604afb647cSTimo Kreuzer float remainderf(float x, float y)
614afb647cSTimo Kreuzer #define _FUNCNAME "remainderf"
624afb647cSTimo Kreuzer #define _OPERATION OP_REM
634afb647cSTimo Kreuzer #endif
644afb647cSTimo Kreuzer {
654afb647cSTimo Kreuzer   double dx, dy, scale, w, t;
664afb647cSTimo Kreuzer   int i, ntimes, xexp, yexp;
67*9e8ed3f8STimo Kreuzer   unsigned long long ux, uy, ax, ay;
684afb647cSTimo Kreuzer 
694afb647cSTimo Kreuzer   unsigned int sw;
704afb647cSTimo Kreuzer 
714afb647cSTimo Kreuzer   dx = x;
724afb647cSTimo Kreuzer   dy = y;
734afb647cSTimo Kreuzer 
744afb647cSTimo Kreuzer 
754afb647cSTimo Kreuzer   GET_BITS_DP64(dx, ux);
764afb647cSTimo Kreuzer   GET_BITS_DP64(dy, uy);
774afb647cSTimo Kreuzer   ax = ux & ~SIGNBIT_DP64;
784afb647cSTimo Kreuzer   ay = uy & ~SIGNBIT_DP64;
794afb647cSTimo Kreuzer   xexp = (int)((ux & EXPBITS_DP64) >> EXPSHIFTBITS_DP64);
804afb647cSTimo Kreuzer   yexp = (int)((uy & EXPBITS_DP64) >> EXPSHIFTBITS_DP64);
814afb647cSTimo Kreuzer 
824afb647cSTimo Kreuzer   if (xexp < 1 || xexp > BIASEDEMAX_DP64 ||
834afb647cSTimo Kreuzer       yexp < 1 || yexp > BIASEDEMAX_DP64)
844afb647cSTimo Kreuzer     {
854afb647cSTimo Kreuzer       /* x or y is zero, NaN or infinity (neither x nor y can be
864afb647cSTimo Kreuzer          denormalized because we promoted from float to double) */
874afb647cSTimo Kreuzer       if (xexp > BIASEDEMAX_DP64)
884afb647cSTimo Kreuzer         {
894afb647cSTimo Kreuzer           /* x is NaN or infinity */
904afb647cSTimo Kreuzer           if (ux & MANTBITS_DP64)
914afb647cSTimo Kreuzer             {
924afb647cSTimo Kreuzer               /* x is NaN */
934afb647cSTimo Kreuzer               unsigned int ufx;
944afb647cSTimo Kreuzer               GET_BITS_SP32(x, ufx);
954afb647cSTimo Kreuzer               return _handle_errorf(_FUNCNAME, _OPERATION, ufx|0x00400000, _DOMAIN, 0,
964afb647cSTimo Kreuzer                                    EDOM, x, y, 2);
974afb647cSTimo Kreuzer             }
984afb647cSTimo Kreuzer           else
994afb647cSTimo Kreuzer             {
1004afb647cSTimo Kreuzer               /* x is infinity; result is NaN */
1014afb647cSTimo Kreuzer               return _handle_errorf(_FUNCNAME, _OPERATION, INDEFBITPATT_SP32, _DOMAIN,
1024afb647cSTimo Kreuzer                                    AMD_F_INVALID, EDOM, x, y, 2);
1034afb647cSTimo Kreuzer             }
1044afb647cSTimo Kreuzer         }
1054afb647cSTimo Kreuzer       else if (yexp > BIASEDEMAX_DP64)
1064afb647cSTimo Kreuzer         {
1074afb647cSTimo Kreuzer           /* y is NaN or infinity */
1084afb647cSTimo Kreuzer           if (uy & MANTBITS_DP64)
1094afb647cSTimo Kreuzer             {
1104afb647cSTimo Kreuzer               /* y is NaN */
1114afb647cSTimo Kreuzer               unsigned int ufy;
1124afb647cSTimo Kreuzer               GET_BITS_SP32(y, ufy);
1134afb647cSTimo Kreuzer               return _handle_errorf(_FUNCNAME, _OPERATION, ufy|0x00400000, _DOMAIN, 0,
1144afb647cSTimo Kreuzer                                    EDOM, x, y, 2);
1154afb647cSTimo Kreuzer             }
1164afb647cSTimo Kreuzer           else
1174afb647cSTimo Kreuzer             {
1184afb647cSTimo Kreuzer #ifdef _CRTBLD_C9X
1194afb647cSTimo Kreuzer               /* C99 return for y = +-inf is x */
1204afb647cSTimo Kreuzer               return x;
1214afb647cSTimo Kreuzer #else
1224afb647cSTimo Kreuzer               /* y is infinity; result is indefinite */
1234afb647cSTimo Kreuzer               return _handle_errorf(_FUNCNAME, _OPERATION, INDEFBITPATT_SP32, _DOMAIN,
1244afb647cSTimo Kreuzer                                    AMD_F_INVALID, EDOM, x, y, 2);
1254afb647cSTimo Kreuzer #endif
1264afb647cSTimo Kreuzer             }
1274afb647cSTimo Kreuzer         }
1284afb647cSTimo Kreuzer       else if (xexp < 1)
1294afb647cSTimo Kreuzer         {
1304afb647cSTimo Kreuzer           /* x must be zero (cannot be denormalized) */
1314afb647cSTimo Kreuzer           if (yexp < 1)
1324afb647cSTimo Kreuzer             {
1334afb647cSTimo Kreuzer               /* y must be zero (cannot be denormalized) */
1344afb647cSTimo Kreuzer               return _handle_errorf(_FUNCNAME, _OPERATION, INDEFBITPATT_SP32, _DOMAIN,
1354afb647cSTimo Kreuzer                                    AMD_F_INVALID, EDOM, x, y, 2);
1364afb647cSTimo Kreuzer             }
1374afb647cSTimo Kreuzer           else
1384afb647cSTimo Kreuzer               /* C99 return for x = 0 must preserve sign */
1394afb647cSTimo Kreuzer               return x;
1404afb647cSTimo Kreuzer         }
1414afb647cSTimo Kreuzer       else
1424afb647cSTimo Kreuzer         {
1434afb647cSTimo Kreuzer           /* y must be zero */
1444afb647cSTimo Kreuzer           return _handle_errorf(_FUNCNAME, _OPERATION, INDEFBITPATT_SP32, _DOMAIN,
1454afb647cSTimo Kreuzer                                AMD_F_INVALID, EDOM, x, y, 2);
1464afb647cSTimo Kreuzer         }
1474afb647cSTimo Kreuzer     }
1484afb647cSTimo Kreuzer   else if (ax == ay)
1494afb647cSTimo Kreuzer     {
1504afb647cSTimo Kreuzer       /* abs(x) == abs(y); return zero with the sign of x */
1514afb647cSTimo Kreuzer       PUT_BITS_DP64(ux & SIGNBIT_DP64, dx);
1524afb647cSTimo Kreuzer       return (float)dx;
1534afb647cSTimo Kreuzer     }
1544afb647cSTimo Kreuzer 
1554afb647cSTimo Kreuzer   /* Set dx = abs(x), dy = abs(y) */
1564afb647cSTimo Kreuzer   PUT_BITS_DP64(ax, dx);
1574afb647cSTimo Kreuzer   PUT_BITS_DP64(ay, dy);
1584afb647cSTimo Kreuzer 
1594afb647cSTimo Kreuzer   if (ax < ay)
1604afb647cSTimo Kreuzer     {
1614afb647cSTimo Kreuzer       /* abs(x) < abs(y) */
1624afb647cSTimo Kreuzer #if !defined(COMPILING_FMOD)
1634afb647cSTimo Kreuzer       if (dx > 0.5*dy)
1644afb647cSTimo Kreuzer         dx -= dy;
1654afb647cSTimo Kreuzer #endif
1664afb647cSTimo Kreuzer       return (float)(x < 0.0? -dx : dx);
1674afb647cSTimo Kreuzer     }
1684afb647cSTimo Kreuzer 
1694afb647cSTimo Kreuzer   /* Save the current floating-point status word. We need
1704afb647cSTimo Kreuzer      to do this because the remainder function is always
1714afb647cSTimo Kreuzer      exact for finite arguments, but our algorithm causes
1724afb647cSTimo Kreuzer      the inexact flag to be raised. We therefore need to
1734afb647cSTimo Kreuzer      restore the entry status before exiting. */
1744afb647cSTimo Kreuzer   sw = get_fpsw_inline();
1754afb647cSTimo Kreuzer 
1764afb647cSTimo Kreuzer   /* Set ntimes to the number of times we need to do a
1774afb647cSTimo Kreuzer      partial remainder. If the exponent of x is an exact multiple
1784afb647cSTimo Kreuzer      of 24 larger than the exponent of y, and the mantissa of x is
1794afb647cSTimo Kreuzer      less than the mantissa of y, ntimes will be one too large
1804afb647cSTimo Kreuzer      but it doesn't matter - it just means that we'll go round
1814afb647cSTimo Kreuzer      the loop below one extra time. */
1824afb647cSTimo Kreuzer   if (xexp <= yexp)
1834afb647cSTimo Kreuzer     {
1844afb647cSTimo Kreuzer       ntimes = 0;
1854afb647cSTimo Kreuzer       w = dy;
1864afb647cSTimo Kreuzer       scale = 1.0;
1874afb647cSTimo Kreuzer     }
1884afb647cSTimo Kreuzer   else
1894afb647cSTimo Kreuzer     {
1904afb647cSTimo Kreuzer       ntimes = (xexp - yexp) / 24;
1914afb647cSTimo Kreuzer 
1924afb647cSTimo Kreuzer       /* Set w = y * 2^(24*ntimes) */
193*9e8ed3f8STimo Kreuzer       PUT_BITS_DP64((unsigned long long)(ntimes * 24 + EXPBIAS_DP64) << EXPSHIFTBITS_DP64,
1944afb647cSTimo Kreuzer                     scale);
1954afb647cSTimo Kreuzer       w = scale * dy;
1964afb647cSTimo Kreuzer       /* Set scale = 2^(-24) */
197*9e8ed3f8STimo Kreuzer       PUT_BITS_DP64((unsigned long long)(-24 + EXPBIAS_DP64) << EXPSHIFTBITS_DP64,
1984afb647cSTimo Kreuzer                     scale);
1994afb647cSTimo Kreuzer     }
2004afb647cSTimo Kreuzer 
2014afb647cSTimo Kreuzer 
2024afb647cSTimo Kreuzer   /* Each time round the loop we compute a partial remainder.
2034afb647cSTimo Kreuzer      This is done by subtracting a large multiple of w
2044afb647cSTimo Kreuzer      from x each time, where w is a scaled up version of y.
2054afb647cSTimo Kreuzer      The subtraction can be performed exactly when performed
2064afb647cSTimo Kreuzer      in double precision, and the result at each stage can
2074afb647cSTimo Kreuzer      fit exactly in a single precision number. */
2084afb647cSTimo Kreuzer   for (i = 0; i < ntimes; i++)
2094afb647cSTimo Kreuzer     {
2104afb647cSTimo Kreuzer       /* t is the integer multiple of w that we will subtract.
2114afb647cSTimo Kreuzer          We use a truncated value for t. */
2124afb647cSTimo Kreuzer       t = (double)((int)(dx / w));
2134afb647cSTimo Kreuzer       dx -= w * t;
2144afb647cSTimo Kreuzer       /* Scale w down by 2^(-24) for the next iteration */
2154afb647cSTimo Kreuzer       w *= scale;
2164afb647cSTimo Kreuzer     }
2174afb647cSTimo Kreuzer 
2184afb647cSTimo Kreuzer   /* One more time */
2194afb647cSTimo Kreuzer #if defined(COMPILING_FMOD)
2204afb647cSTimo Kreuzer   t = (double)((int)(dx / w));
2214afb647cSTimo Kreuzer   dx -= w * t;
2224afb647cSTimo Kreuzer #else
2234afb647cSTimo Kreuzer  {
2244afb647cSTimo Kreuzer   unsigned int todd;
2254afb647cSTimo Kreuzer   /* Variable todd says whether the integer t is odd or not */
2264afb647cSTimo Kreuzer   t = (double)((int)(dx / w));
2274afb647cSTimo Kreuzer   todd = ((int)(dx / w)) & 1;
2284afb647cSTimo Kreuzer   dx -= w * t;
2294afb647cSTimo Kreuzer 
2304afb647cSTimo Kreuzer   /* At this point, dx lies in the range [0,dy) */
2314afb647cSTimo Kreuzer   /* For the remainder function, we need to adjust dx
2324afb647cSTimo Kreuzer      so that it lies in the range (-y/2, y/2] by carefully
2334afb647cSTimo Kreuzer      subtracting w (== dy == y) if necessary. */
2344afb647cSTimo Kreuzer   if (dx > 0.5 * w || ((dx == 0.5 * w) && todd))
2354afb647cSTimo Kreuzer     dx -= w;
2364afb647cSTimo Kreuzer  }
2374afb647cSTimo Kreuzer #endif
2384afb647cSTimo Kreuzer 
2394afb647cSTimo Kreuzer   /* **** N.B. for some reason this breaks the 32 bit version
2404afb647cSTimo Kreuzer      of remainder when compiling with optimization. */
2414afb647cSTimo Kreuzer   /* Restore the entry status flags */
2424afb647cSTimo Kreuzer   set_fpsw_inline(sw);
2434afb647cSTimo Kreuzer 
2444afb647cSTimo Kreuzer   /* Set the result sign according to input argument x */
2454afb647cSTimo Kreuzer   return (float)(x < 0.0? -dx : dx);
2464afb647cSTimo Kreuzer 
2474afb647cSTimo Kreuzer }
248