Adding some new functions to System.Math and System.MathF (#20788)
authorTanner Gooding <tagoo@outlook.com>
Mon, 5 Nov 2018 23:37:36 +0000 (15:37 -0800)
committerGitHub <noreply@github.com>
Mon, 5 Nov 2018 23:37:36 +0000 (15:37 -0800)
* Adding BitIncrement, BitDecrement, CopySign, MaxMagnitude, and MinMagnitude to Math and MathF

* Adding FusedMultiplyAdd, IlogB, Log2, and ScaleB to Math and MathF

* Adding some basic PAL tests for fma, ilogb, log2, and scalbn

* Fixing a couple typos and adding clarifying comments

* Fixing the MSVC _VVV FCALL declarations

47 files changed:
src/System.Private.CoreLib/shared/System/Math.cs
src/System.Private.CoreLib/shared/System/MathF.cs
src/System.Private.CoreLib/src/System/Math.CoreCLR.cs
src/System.Private.CoreLib/src/System/MathF.CoreCLR.cs
src/classlibnative/float/floatdouble.cpp
src/classlibnative/float/floatsingle.cpp
src/classlibnative/inc/floatdouble.h
src/classlibnative/inc/floatsingle.h
src/jit/importer.cpp
src/jit/lsraxarch.cpp
src/pal/inc/pal.h
src/pal/src/cruntime/math.cpp
src/pal/src/include/pal/palinternal.h
src/pal/tests/palsuite/c_runtime/fma/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/fma/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/fma/test1/test1.cpp [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/fma/test1/testinfo.dat [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/fmaf/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/fmaf/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/fmaf/test1/test1.c [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/fmaf/test1/testinfo.dat [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/ilogb/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/ilogb/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/ilogb/test1/test1.cpp [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/ilogb/test1/testinfo.dat [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/ilogbf/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/ilogbf/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/ilogbf/test1/test1.c [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/ilogbf/test1/testinfo.dat [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/log2/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/log2/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/log2/test1/test1.cpp [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/log2/test1/testinfo.dat [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/log2f/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/log2f/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/log2f/test1/test1.c [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/log2f/test1/testinfo.dat [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/scalbn/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/scalbn/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/scalbn/test1/test1.cpp [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/scalbn/test1/testinfo.dat [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/scalbnf/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/scalbnf/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/scalbnf/test1/test1.c [new file with mode: 0644]
src/pal/tests/palsuite/c_runtime/scalbnf/test1/testinfo.dat [new file with mode: 0644]
src/vm/ecalllist.h
src/vm/fcall.h

index 6227311..41a0806 100644 (file)
@@ -110,6 +110,76 @@ namespace System
             return ((long)a) * b;
         }
 
+        public static double BitDecrement(double x)
+        {
+            var bits = BitConverter.DoubleToInt64Bits(x);
+
+            if (((bits >> 32) & 0x7FF00000) >= 0x7FF00000)
+            {
+                // NaN returns NaN
+                // -Infinity returns -Infinity
+                // +Infinity returns double.MaxValue
+                return (bits == 0x7FF00000_00000000) ? double.MaxValue : x;
+            }
+
+            if (bits == 0x00000000_00000000)
+            {
+                // +0.0 returns -double.Epsilon
+                return -double.Epsilon;
+            }
+
+            // Negative values need to be incremented
+            // Positive values need to be decremented
+
+            bits += ((bits < 0) ? +1 : -1);
+            return BitConverter.Int64BitsToDouble(bits);
+        }
+
+        public static double BitIncrement(double x)
+        {
+            var bits = BitConverter.DoubleToInt64Bits(x);
+
+            if (((bits >> 32) & 0x7FF00000) >= 0x7FF00000)
+            {
+                // NaN returns NaN
+                // -Infinity returns double.MinValue
+                // +Infinity returns +Infinity
+                return (bits == unchecked((long)(0xFFF00000_00000000))) ? double.MinValue : x;
+            }
+
+            if (bits == unchecked((long)(0x80000000_00000000)))
+            {
+                // -0.0 returns double.Epsilon
+                return double.Epsilon;
+            }
+
+            // Negative values need to be decremented
+            // Positive values need to be incremented
+
+            bits += ((bits < 0) ? -1 : +1);
+            return BitConverter.Int64BitsToDouble(bits);
+        }
+
+        public static unsafe double CopySign(double x, double y)
+        {
+            // This method is required to work for all inputs,
+            // including NaN, so we operate on the raw bits.
+
+            var xbits = BitConverter.DoubleToInt64Bits(x);
+            var ybits = BitConverter.DoubleToInt64Bits(y);
+
+            // If the sign bits of x and y are not the same,
+            // flip the sign bit of x and return the new value;
+            // otherwise, just return x
+
+            if ((xbits ^ ybits) < 0)
+            {
+                return BitConverter.Int64BitsToDouble(xbits ^ long.MinValue);
+            }
+
+            return x;
+        }
+
         public static int DivRem(int a, int b, out int result)
         {
             // TODO https://github.com/dotnet/coreclr/issues/3439:
@@ -542,6 +612,11 @@ namespace System
             return (val1 >= val2) ? val1 : val2;
         }
 
+        public static double MaxMagnitude(double x, double y)
+        {
+            return Max(Abs(x), Abs(y));
+        }
+
         [NonVersionable]
         public static byte Min(byte val1, byte val2)
         {
@@ -630,6 +705,11 @@ namespace System
             return (val1 <= val2) ? val1 : val2;
         }
 
+        public static double MinMagnitude(double x, double y)
+        {
+            return Min(Abs(x), Abs(y));
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static decimal Round(decimal d)
         {
@@ -680,7 +760,7 @@ namespace System
                 flrTempVal -= 1.0;
             }
 
-            return copysign(flrTempVal, a);
+            return CopySign(flrTempVal, a);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -810,23 +890,6 @@ namespace System
             return d;
         }
 
-        private static unsafe double copysign(double x, double y)
-        {
-            var xbits = BitConverter.DoubleToInt64Bits(x);
-            var ybits = BitConverter.DoubleToInt64Bits(y);
-
-            // If the sign bits of x and y are not the same,
-            // flip the sign bit of x and return the new value;
-            // otherwise, just return x
-
-            if (((xbits ^ ybits) >> 63) != 0)
-            {
-                return BitConverter.Int64BitsToDouble(xbits ^ long.MinValue);
-            }
-
-            return x;
-        }
-
         private static void ThrowMinMaxException<T>(T min, T max)
         {
             throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max));
index 7769023..9eac890 100644 (file)
@@ -36,6 +36,76 @@ namespace System
             return Math.Abs(x);
         }
 
+        public static float BitDecrement(float x)
+        {
+            var bits = BitConverter.SingleToInt32Bits(x);
+
+            if ((bits & 0x7F800000) >= 0x7F800000)
+            {
+                // NaN returns NaN
+                // -Infinity returns -Infinity
+                // +Infinity returns float.MaxValue
+                return (bits == 0x7F800000) ? float.MaxValue : x;
+            }
+
+            if (bits == 0x00000000)
+            {
+                // +0.0 returns -float.Epsilon
+                return -float.Epsilon;
+            }
+
+            // Negative values need to be incremented
+            // Positive values need to be decremented
+
+            bits += ((bits < 0) ? +1 : -1);
+            return BitConverter.Int32BitsToSingle(bits);
+        }
+
+        public static float BitIncrement(float x)
+        {
+            var bits = BitConverter.SingleToInt32Bits(x);
+
+            if ((bits & 0x7F800000) >= 0x7F800000)
+            {
+                // NaN returns NaN
+                // -Infinity returns float.MinValue
+                // +Infinity returns +Infinity
+                return (bits == unchecked((int)(0xFF800000))) ? float.MinValue : x;
+            }
+
+            if (bits == unchecked((int)(0x80000000)))
+            {
+                // -0.0 returns float.Epsilon
+                return float.Epsilon;
+            }
+
+            // Negative values need to be decremented
+            // Positive values need to be incremented
+
+            bits += ((bits < 0) ? -1 : +1);
+            return BitConverter.Int32BitsToSingle(bits);
+        }
+
+        public static unsafe float CopySign(float x, float y)
+        {
+            // This method is required to work for all inputs,
+            // including NaN, so we operate on the raw bits.
+
+            var xbits = BitConverter.SingleToInt32Bits(x);
+            var ybits = BitConverter.SingleToInt32Bits(y);
+
+            // If the sign bits of x and y are not the same,
+            // flip the sign bit of x and return the new value;
+            // otherwise, just return x
+
+            if ((xbits ^ ybits) < 0)
+            {
+                return BitConverter.Int32BitsToSingle(xbits ^ int.MinValue);
+            }
+
+            return x;
+        }
+
         public static float IEEERemainder(float x, float y)
         {
             if (float.IsNaN(x))
@@ -118,12 +188,22 @@ namespace System
             return Math.Max(x, y);
         }
 
+        public static float MaxMagnitude(float x, float y)
+        {
+            return Max(Abs(x), Abs(y));
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static float Min(float x, float y)
         {
             return Math.Min(x, y);
         }
 
+        public static float MinMagnitude(float x, float y)
+        {
+            return Min(Abs(x), Abs(y));
+        }
+
         [Intrinsic]
         public static float Round(float x)
         {
@@ -214,22 +294,5 @@ namespace System
             ModF(x, &x);
             return x;
         }
-
-        private static unsafe float CopySign(float x, float y)
-        {
-            var xbits = BitConverter.SingleToInt32Bits(x);
-            var ybits = BitConverter.SingleToInt32Bits(y);
-
-            // If the sign bits of x and y are not the same,
-            // flip the sign bit of x and return the new value;
-            // otherwise, just return x
-
-            if (((xbits ^ ybits) >> 31) != 0)
-            {
-                return BitConverter.Int32BitsToSingle(xbits ^ int.MinValue);
-            }
-
-            return x;
-        }
     }
 }
index e4c85f9..5331353 100644 (file)
@@ -65,15 +65,27 @@ namespace System
         public static extern double Floor(double d);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern double FusedMultiplyAdd(double x, double y, double z);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern int IlogB(double x);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern double Log(double d);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern double Log2(double x);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern double Log10(double d);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern double Pow(double x, double y);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern double ScaleB(double x, int n);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern double Sin(double a);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
index 1abc040..fa99f1f 100644 (file)
@@ -56,15 +56,27 @@ namespace System
         public static extern float Floor(float x);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern float FusedMultiplyAdd(float x, float y, float z);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern int IlogB(float x);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern float Log(float x);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern float Log2(float x);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern float Log10(float x);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern float Pow(float x, float y);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern float ScaleB(float x, int n);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern float Sin(float x);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
index b27ef61..2e4b382 100644 (file)
@@ -200,6 +200,24 @@ FCIMPL2_VV(double, COMDouble::FMod, double x, double y)
     return (double)fmod(x, y);
 FCIMPLEND
 
+/*=====================================FusedMultiplyAdd==========================
+**
+==============================================================================*/
+FCIMPL3_VVV(double, COMDouble::FusedMultiplyAdd, double x, double y, double z)
+    FCALL_CONTRACT;
+
+    return (double)fma(x, y, z);
+FCIMPLEND
+
+/*=====================================Ilog2====================================
+**
+==============================================================================*/
+FCIMPL1_V(int, COMDouble::IlogB, double x)
+    FCALL_CONTRACT;
+
+    return (int)ilogb(x);
+FCIMPLEND
+
 /*=====================================Log======================================
 **
 ==============================================================================*/
@@ -209,6 +227,15 @@ FCIMPL1_V(double, COMDouble::Log, double x)
     return (double)log(x);
 FCIMPLEND
 
+/*=====================================Log2=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Log2, double x)
+    FCALL_CONTRACT;
+
+    return (double)log2(x);
+FCIMPLEND
+
 /*====================================Log10=====================================
 **
 ==============================================================================*/
@@ -236,6 +263,15 @@ FCIMPL2_VV(double, COMDouble::Pow, double x, double y)
     return (double)pow(x, y);
 FCIMPLEND
 
+/*=====================================ScaleB===================================
+**
+==============================================================================*/
+FCIMPL2_VI(double, COMDouble::ScaleB, double x, int n)
+    FCALL_CONTRACT;
+
+    return (double)scalbn(x, n);
+FCIMPLEND
+
 /*=====================================Sin======================================
 **
 ==============================================================================*/
index 3903bc9..23c1445 100644 (file)
@@ -187,6 +187,24 @@ FCIMPL2_VV(float, COMSingle::FMod, float x, float y)
     return (float)fmodf(x, y);
 FCIMPLEND
 
+/*=====================================FusedMultiplyAdd==========================
+**
+==============================================================================*/
+FCIMPL3_VVV(float, COMSingle::FusedMultiplyAdd, float x, float y, float z)
+    FCALL_CONTRACT;
+
+    return (float)fmaf(x, y, z);
+FCIMPLEND
+
+/*=====================================Ilog2====================================
+**
+==============================================================================*/
+FCIMPL1_V(int, COMSingle::IlogB, float x)
+    FCALL_CONTRACT;
+
+    return (int)ilogbf(x);
+FCIMPLEND
+
 /*=====================================Log======================================
 **
 ==============================================================================*/
@@ -196,6 +214,15 @@ FCIMPL1_V(float, COMSingle::Log, float x)
     return (float)logf(x);
 FCIMPLEND
 
+/*=====================================Log2=====================================
+**
+==============================================================================*/
+FCIMPL1_V(float, COMSingle::Log2, float x)
+    FCALL_CONTRACT;
+
+    return (float)log2f(x);
+FCIMPLEND
+
 /*====================================Log10=====================================
 **
 ==============================================================================*/
@@ -223,6 +250,15 @@ FCIMPL2_VV(float, COMSingle::Pow, float x, float y)
     return (float)powf(x, y);
 FCIMPLEND
 
+/*=====================================ScaleB===================================
+**
+==============================================================================*/
+FCIMPL2_VI(float, COMSingle::ScaleB, float x, int n)
+    FCALL_CONTRACT;
+
+    return (float)scalbnf(x, n);
+FCIMPLEND
+
 /*=====================================Sin======================================
 **
 ==============================================================================*/
index d2c819f..602b45b 100644 (file)
@@ -25,10 +25,14 @@ public:
     FCDECL1_V(static double, Exp, double x);
     FCDECL1_V(static double, Floor, double x);
     FCDECL2_VV(static double, FMod, double x, double y);
+    FCDECL3_VVV(static double, FusedMultiplyAdd, double x, double y, double z);
+    FCDECL1_V(static int, IlogB, double x);
     FCDECL1_V(static double, Log, double x);
+    FCDECL1_V(static double, Log2, double x);
     FCDECL1_V(static double, Log10, double x);
     FCDECL2_VI(static double, ModF, double x, double* intptr);
     FCDECL2_VV(static double, Pow, double x, double y);
+    FCDECL2_VI(static double, ScaleB, double x, int n);
     FCDECL1_V(static double, Sin, double x);
     FCDECL1_V(static double, Sinh, double x);
     FCDECL1_V(static double, Sqrt, double x);
index a4f9cb7..f54a650 100644 (file)
@@ -25,10 +25,14 @@ public:
     FCDECL1_V(static float, Exp, float x);
     FCDECL1_V(static float, Floor, float x);
     FCDECL2_VV(static float, FMod, float x, float y);
+    FCDECL3_VVV(static float, FusedMultiplyAdd, float x, float y, float z);
+    FCDECL1_V(static int, IlogB, float x);
     FCDECL1_V(static float, Log, float x);
+    FCDECL1_V(static float, Log2, float x);
     FCDECL1_V(static float, Log10, float x);
     FCDECL2_VI(static float, ModF, float x, float* intptr);
     FCDECL2_VV(static float, Pow, float x, float y);
+    FCDECL2_VI(static float, ScaleB, float x, int n);
     FCDECL1_V(static float, Sin, float x);
     FCDECL1_V(static float, Sinh, float x);
     FCDECL1_V(static float, Sqrt, float x);
index 94b78ce..5a91904 100644 (file)
@@ -3964,17 +3964,7 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method,
     GenTree* op2;
 
     assert(callType != TYP_STRUCT);
-    assert((intrinsicID == CORINFO_INTRINSIC_Sin) || intrinsicID == CORINFO_INTRINSIC_Cbrt ||
-           (intrinsicID == CORINFO_INTRINSIC_Sqrt) || (intrinsicID == CORINFO_INTRINSIC_Abs) ||
-           (intrinsicID == CORINFO_INTRINSIC_Cos) || (intrinsicID == CORINFO_INTRINSIC_Round) ||
-           (intrinsicID == CORINFO_INTRINSIC_Cosh) || (intrinsicID == CORINFO_INTRINSIC_Sinh) ||
-           (intrinsicID == CORINFO_INTRINSIC_Tan) || (intrinsicID == CORINFO_INTRINSIC_Tanh) ||
-           (intrinsicID == CORINFO_INTRINSIC_Asin) || (intrinsicID == CORINFO_INTRINSIC_Asinh) ||
-           (intrinsicID == CORINFO_INTRINSIC_Acos) || (intrinsicID == CORINFO_INTRINSIC_Acosh) ||
-           (intrinsicID == CORINFO_INTRINSIC_Atan) || (intrinsicID == CORINFO_INTRINSIC_Atan2) ||
-           (intrinsicID == CORINFO_INTRINSIC_Atanh) || (intrinsicID == CORINFO_INTRINSIC_Log10) ||
-           (intrinsicID == CORINFO_INTRINSIC_Pow) || (intrinsicID == CORINFO_INTRINSIC_Exp) ||
-           (intrinsicID == CORINFO_INTRINSIC_Ceiling) || (intrinsicID == CORINFO_INTRINSIC_Floor));
+    assert(IsMathIntrinsic(intrinsicID));
 
     op1 = nullptr;
 
index 5c37aba..530a71c 100644 (file)
@@ -1797,9 +1797,6 @@ int LinearScan::BuildIntrinsic(GenTree* tree)
 
     switch (tree->gtIntrinsic.gtIntrinsicId)
     {
-        case CORINFO_INTRINSIC_Sqrt:
-            break;
-
         case CORINFO_INTRINSIC_Abs:
             // Abs(float x) = x & 0x7fffffff
             // Abs(double x) = x & 0x7ffffff ffffffff
@@ -1826,6 +1823,7 @@ int LinearScan::BuildIntrinsic(GenTree* tree)
             break;
 #endif // _TARGET_X86_
 
+        case CORINFO_INTRINSIC_Sqrt:
         case CORINFO_INTRINSIC_Round:
         case CORINFO_INTRINSIC_Ceiling:
         case CORINFO_INTRINSIC_Floor:
index 6908040..a7f0ff5 100644 (file)
@@ -4158,18 +4158,26 @@ SetThreadIdealProcessorEx(
 #define asinh         PAL_asinh
 #define atan2         PAL_atan2
 #define exp           PAL_exp
+#define fma           PAL_fma
+#define ilogb         PAL_ilogb
 #define log           PAL_log
+#define log2          PAL_log2
 #define log10         PAL_log10
 #define pow           PAL_pow
+#define scalbn        PAL_scalbn
 #define acosf         PAL_acosf
 #define acoshf        PAL_acoshf
 #define asinf         PAL_asinf
 #define asinhf        PAL_asinhf
 #define atan2f        PAL_atan2f
 #define expf          PAL_expf
+#define fmaf          PAL_fmaf
+#define ilogbf        PAL_ilogbf
 #define logf          PAL_logf
+#define log2f         PAL_log2f
 #define log10f        PAL_log10f
 #define powf          PAL_powf
+#define scalbnf       PAL_scalbnf
 #define malloc        PAL_malloc
 #define free          PAL_free
 #define mkstemp       PAL_mkstemp
@@ -4424,10 +4432,14 @@ PALIMPORT double __cdecl exp(double);
 PALIMPORT double __cdecl fabs(double);
 PALIMPORT double __cdecl floor(double);
 PALIMPORT double __cdecl fmod(double, double); 
+PALIMPORT double __cdecl fma(double, double, double);
+PALIMPORT int __cdecl ilogb(double);
 PALIMPORT double __cdecl log(double);
+PALIMPORT double __cdecl log2(double);
 PALIMPORT double __cdecl log10(double);
 PALIMPORT double __cdecl modf(double, double*);
 PALIMPORT double __cdecl pow(double, double);
+PALIMPORT double __cdecl scalbn(double, int);
 PALIMPORT double __cdecl sin(double);
 PALIMPORT double __cdecl sinh(double);
 PALIMPORT double __cdecl sqrt(double);
@@ -4452,11 +4464,15 @@ PALIMPORT float __cdecl coshf(float);
 PALIMPORT float __cdecl expf(float);
 PALIMPORT float __cdecl fabsf(float);
 PALIMPORT float __cdecl floorf(float);
-PALIMPORT float __cdecl fmodf(float, float); 
+PALIMPORT float __cdecl fmodf(float, float);
+PALIMPORT float __cdecl fmaf(float, float, float);
+PALIMPORT int __cdecl ilogbf(float);
 PALIMPORT float __cdecl logf(float);
+PALIMPORT float __cdecl log2f(float);
 PALIMPORT float __cdecl log10f(float);
 PALIMPORT float __cdecl modff(float, float*);
 PALIMPORT float __cdecl powf(float, float);
+PALIMPORT float __cdecl scalbnf(float, int);
 PALIMPORT float __cdecl sinf(float);
 PALIMPORT float __cdecl sinhf(float);
 PALIMPORT float __cdecl sqrtf(float);
index af2f994..126bbff 100644 (file)
@@ -311,6 +311,44 @@ PALIMPORT double __cdecl PAL_exp(double x)
 
 /*++
 Function:
+    fma
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_fma(double x, double y, double z)
+{
+    double ret;
+    PERF_ENTRY(fma);
+    ENTRY("fma (x=%f, y=%f, z=%f)\n", x, y, z);
+
+    ret = fma(x, y, z);
+
+    LOGEXIT("fma returns double %f\n", ret);
+    PERF_EXIT(fma);
+    return ret;
+}
+
+/*++
+Function:
+    ilogb
+
+See MSDN.
+--*/
+PALIMPORT int __cdecl PAL_ilogb(double x)
+{
+    int ret;
+    PERF_ENTRY(ilogb);
+    ENTRY("ilogb (x=%f)\n", x);
+
+    ret = ilogb(x);
+
+    LOGEXIT("ilogb returns int %d\n", ret);
+    PERF_EXIT(ilogb);
+    return ret;
+}
+
+/*++
+Function:
     labs
 
 See MSDN.
@@ -360,6 +398,25 @@ PALIMPORT double __cdecl PAL_log(double x)
 
 /*++
 Function:
+    log2
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_log2(double x)
+{
+    double ret;
+    PERF_ENTRY(log2);
+    ENTRY("log2 (x=%f)\n", x);
+
+    ret = log2(x);
+
+    LOGEXIT("log2 returns double %f\n", ret);
+    PERF_EXIT(log2);
+    return ret;
+}
+
+/*++
+Function:
     log10
 
 See MSDN.
@@ -483,6 +540,25 @@ PALIMPORT double __cdecl PAL_pow(double x, double y)
 
 /*++
 Function:
+    scalbn
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_scalbn(double x, int n)
+{
+    double ret;
+    PERF_ENTRY(scalbn);
+    ENTRY("scalbn (x=%f, n=%d)\n", x, n);
+
+    ret = scalbn(x, n);
+
+    LOGEXIT("scalbn returns double %f\n", ret);
+    PERF_EXIT(scalbn);
+    return ret;
+}
+
+/*++
+Function:
   _signbitf
 
 Determines whether given single-precision floating point value has a negative sign.
@@ -750,6 +826,44 @@ PALIMPORT float __cdecl PAL_expf(float x)
 
 /*++
 Function:
+    fmaf
+
+See MSDN.
+--*/
+PALIMPORT float __cdecl PAL_fmaf(float x, float y, float z)
+{
+    float ret;
+    PERF_ENTRY(fmaf);
+    ENTRY("fmaf (x=%f, y=%f, z=%f)\n", x, y, z);
+
+    ret = fmaf(x, y, z);
+
+    LOGEXIT("fma returns float %f\n", ret);
+    PERF_EXIT(fmaf);
+    return ret;
+}
+
+/*++
+Function:
+    ilogbf
+
+See MSDN.
+--*/
+PALIMPORT int __cdecl PAL_ilogbf(float x)
+{
+    int ret;
+    PERF_ENTRY(ilogbf);
+    ENTRY("ilogbf (x=%f)\n", x);
+
+    ret = ilogbf(x);
+
+    LOGEXIT("ilogbf returns int %d\n", ret);
+    PERF_EXIT(ilogbf);
+    return ret;
+}
+
+/*++
+Function:
     logf
 
 See MSDN.
@@ -780,6 +894,25 @@ PALIMPORT float __cdecl PAL_logf(float x)
 
 /*++
 Function:
+    log2f
+
+See MSDN.
+--*/
+PALIMPORT float __cdecl PAL_log2f(float x)
+{
+    float ret;
+    PERF_ENTRY(log2f);
+    ENTRY("log2f (x=%f)\n", x);
+
+    ret = log2f(x);
+
+    LOGEXIT("log2f returns float %f\n", ret);
+    PERF_EXIT(log2f);
+    return ret;
+}
+
+/*++
+Function:
     log10f
 
 See MSDN.
@@ -894,3 +1027,22 @@ PALIMPORT float __cdecl PAL_powf(float x, float y)
     PERF_EXIT(powf);
     return ret;
 }
+
+/*++
+Function:
+    scalbnf
+
+See MSDN.
+--*/
+PALIMPORT float __cdecl PAL_scalbnf(float x, int n)
+{
+    float ret;
+    PERF_ENTRY(scalbnf);
+    ENTRY("scalbnf (x=%f, n=%d)\n", x, n);
+
+    ret = scalbnf(x, n);
+
+    LOGEXIT("scalbnf returns double %f\n", ret);
+    PERF_EXIT(scalbnf);
+    return ret;
+}
index 69e4f5f..0e37ba8 100644 (file)
@@ -461,10 +461,14 @@ function_name() to call the system's implementation
 #undef fabs
 #undef floor
 #undef fmod
+#undef fma
+#undef ilogb
 #undef log
+#undef log2
 #undef log10
 #undef modf
 #undef pow
+#undef scalbn
 #undef sin
 #undef sinh
 #undef sqrt
@@ -485,10 +489,14 @@ function_name() to call the system's implementation
 #undef fabsf
 #undef floorf
 #undef fmodf
+#undef fmaf
+#undef ilogbf
 #undef logf
+#undef log2f
 #undef log10f
 #undef modff
 #undef powf
+#undef scalbnf
 #undef sinf
 #undef sinhf
 #undef sqrtf
diff --git a/src/pal/tests/palsuite/c_runtime/fma/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/fma/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e1ef7f
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/c_runtime/fma/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/fma/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..bb478cb
--- /dev/null
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+  test1.cpp
+)
+
+add_executable(paltest_fma_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_fma_test1 coreclrpal)
+
+target_link_libraries(paltest_fma_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/c_runtime/fma/test1/test1.cpp b/src/pal/tests/palsuite/c_runtime/fma/test1/test1.cpp
new file mode 100644 (file)
index 0000000..f6918d6
--- /dev/null
@@ -0,0 +1,151 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source:  test1.c
+**
+** Purpose: Tests that fma returns correct values for a subset of values.
+**          Tests with positive and negative values of x, y, and z to ensure
+**          fmaf is returning correct results.
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+// binary64 (double) has a machine epsilon of 2^-52 (approx. 2.22e-16). However, this 
+// is slightly too accurate when writing tests meant to run against libm implementations
+// for various platforms. 2^-50 (approx. 8.88e-16) seems to be as accurate as we can get.
+//
+// The tests themselves will take PAL_EPSILON and adjust it according to the expected result
+// so that the delta used for comparison will compare the most significant digits and ignore
+// any digits that are outside the double precision range (15-17 digits).
+
+// For example, a test with an expect result in the format of 0.xxxxxxxxxxxxxxxxx will use
+// PAL_EPSILON for the variance, while an expected result in the format of 0.0xxxxxxxxxxxxxxxxx
+// will use PAL_EPSILON / 10 and and expected result in the format of x.xxxxxxxxxxxxxxxx will
+// use PAL_EPSILON * 10.
+#define PAL_EPSILON 8.8817841970012523e-16
+
+#define PAL_NAN     sqrt(-1.0)
+#define PAL_POSINF -log(0.0)
+#define PAL_NEGINF  log(0.0)
+
+/**
+ * Helper test structure
+ */
+struct test
+{
+    double x;         /* first component of the value to test the function with */
+    double y;         /* second component of the value to test the function with */
+    double z;         /* third component of the value to test the function with */
+    double expected;  /* expected result */
+    double variance;  /* maximum delta between the expected and actual result */
+};
+
+/**
+ * validate
+ *
+ * test validation function
+ */
+void __cdecl validate(double x, double y, double z, double expected, double variance)
+{
+    double result = fma(x, y, z);
+
+    /*
+     * The test is valid when the difference between result
+     * and expected is less than or equal to variance
+     */
+    double delta = fabs(result - expected);
+
+    if (delta > variance)
+    {
+        Fail("fma(%g, %g, %g) returned %20.17g when it should have returned %20.17g",
+             x, y, z, result, expected);
+    }
+}
+
+/**
+ * validate
+ *
+ * test validation function for values returning NaN
+ */
+void __cdecl validate_isnan(double x, double y, double z)
+{
+    double result = fma(x, y, z);
+
+    if (!_isnan(result))
+    {
+        Fail("fma(%g, %g, %g) returned %20.17g when it should have returned %20.17g",
+             x, y, z, result, PAL_NAN);
+    }
+}
+
+/**
+ * main
+ * 
+ * executable entry point
+ */
+int __cdecl main(int argc, char **argv)
+{
+    struct test tests[] = 
+    {
+        /* x                       y                       z                       expected                   variance */
+        {  PAL_NEGINF,             PAL_NEGINF,             PAL_NEGINF,             PAL_NEGINF,                0 },
+        { -1e308,                  2,                      1e300,                 -1e300,                     0 },
+        {  1e308,                  2,                     -1e300,                  1e300,                     0 },
+        {  PAL_POSINF,             PAL_POSINF,             PAL_POSINF,             PAL_POSINF,                0 },
+    };
+
+    if (PAL_Initialize(argc, argv) != 0)
+    {
+        return FAIL;
+    }
+
+    for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
+    {
+        validate(tests[i].x, tests[i].y, tests[i].z, tests[i].expected, tests[i].variance);
+    }
+
+    // Returns NaN if x or y is infinite, the other is zero, and z is NaN
+    validate_isnan(PAL_NEGINF, 0, PAL_NAN);
+    validate_isnan(PAL_POSINF, 0, PAL_NAN);
+    validate_isnan(0, PAL_NEGINF, PAL_NAN);
+    validate_isnan(0, PAL_POSINF, PAL_NAN);
+
+    // Returns NaN if x or y is infinite, the other is zero, and z is not-NaN
+    validate_isnan(PAL_POSINF, 0, PAL_NEGINF);
+    validate_isnan(PAL_NEGINF, 0, PAL_NEGINF);
+    validate_isnan(0, PAL_POSINF, PAL_NEGINF);
+    validate_isnan(0, PAL_NEGINF, PAL_NEGINF);
+    
+    validate_isnan(PAL_POSINF, 0, 0);
+    validate_isnan(PAL_NEGINF, 0, 0);
+    validate_isnan(0, PAL_POSINF, 0);
+    validate_isnan(0, PAL_NEGINF, 0);
+
+    validate_isnan(PAL_POSINF, 0, PAL_POSINF);
+    validate_isnan(PAL_NEGINF, 0, PAL_POSINF);
+    validate_isnan(0, PAL_POSINF, PAL_POSINF);
+    validate_isnan(0, PAL_NEGINF, PAL_POSINF);
+
+    // Returns NaN if (x * y) is infinite, and z is an infinite of the opposite sign
+    validate_isnan(PAL_POSINF, PAL_POSINF, PAL_NEGINF);
+    validate_isnan(PAL_NEGINF, PAL_NEGINF, PAL_POSINF);
+    validate_isnan(PAL_POSINF, PAL_NEGINF, PAL_POSINF);
+    validate_isnan(PAL_NEGINF, PAL_POSINF, PAL_POSINF);
+
+    validate_isnan(PAL_POSINF, 1, PAL_NEGINF);
+    validate_isnan(PAL_NEGINF, 1, PAL_POSINF);
+    validate_isnan(PAL_POSINF, 1, PAL_POSINF);
+    validate_isnan(PAL_NEGINF, 1, PAL_POSINF);
+
+    validate_isnan(1, PAL_POSINF, PAL_NEGINF);
+    validate_isnan(1, PAL_NEGINF, PAL_POSINF);
+    validate_isnan(1, PAL_NEGINF, PAL_POSINF);
+    validate_isnan(1, PAL_POSINF, PAL_POSINF);
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/c_runtime/fma/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/fma/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..22bf0e7
--- /dev/null
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = C Runtime
+Function = fma
+Name = Call fma with some std input/output.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Call the fma function with various num/exponent pairs
+= that should produce std answers.
+
+
+
+
diff --git a/src/pal/tests/palsuite/c_runtime/fmaf/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/fmaf/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e1ef7f
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/c_runtime/fmaf/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/fmaf/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d723d32
--- /dev/null
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+  test1.c
+)
+
+add_executable(paltest_fmaf_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_fmaf_test1 coreclrpal)
+
+target_link_libraries(paltest_fmaf_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/c_runtime/fmaf/test1/test1.c b/src/pal/tests/palsuite/c_runtime/fmaf/test1/test1.c
new file mode 100644 (file)
index 0000000..7c3b5e3
--- /dev/null
@@ -0,0 +1,150 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source:  test1.c
+**
+** Purpose: Tests that fmaf returns correct values for a subset of values.
+**          Tests with positive and negative values of x, y, and z to ensure
+**          fmaf is returning correct results.
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+// binary32 (float) has a machine epsilon of 2^-23 (approx. 1.19e-07). However, this 
+// is slightly too accurate when writing tests meant to run against libm implementations
+// for various platforms. 2^-21 (approx. 4.76e-07) seems to be as accurate as we can get.
+//
+// The tests themselves will take PAL_EPSILON and adjust it according to the expected result
+// so that the delta used for comparison will compare the most significant digits and ignore
+// any digits that are outside the double precision range (6-9 digits).
+
+// For example, a test with an expect result in the format of 0.xxxxxxxxx will use PAL_EPSILON
+// for the variance, while an expected result in the format of 0.0xxxxxxxxx will use
+// PAL_EPSILON / 10 and and expected result in the format of x.xxxxxx will use PAL_EPSILON * 10.
+#define PAL_EPSILON 4.76837158e-07
+
+#define PAL_NAN     sqrtf(-1.0f)
+#define PAL_POSINF -logf(0.0f)
+#define PAL_NEGINF  logf(0.0f)
+
+/**
+ * Helper test structure
+ */
+struct test
+{
+    float x;         /* first component of the value to test the function with */
+    float y;         /* second component of the value to test the function with */
+    float z;         /* third component of the value to test the function with */
+    float expected;  /* expected result */
+    float variance;  /* maximum delta between the expected and actual result */
+};
+
+/**
+ * validate
+ *
+ * test validation function
+ */
+void __cdecl validate(float x, float y, float z, float expected, float variance)
+{
+    float result = fmaf(x, y, z);
+
+    /*
+     * The test is valid when the difference between result
+     * and expected is less than or equal to variance
+     */
+    float delta = fabsf(result - expected);
+
+    if (delta > variance)
+    {
+        Fail("fmaf(%g, %g, %g) returned %10.9g when it should have returned %10.9g",
+             x, y, z, result, expected);
+    }
+}
+
+/**
+ * validate
+ *
+ * test validation function for values returning NaN
+ */
+void __cdecl validate_isnan(float x, float y, float z)
+{
+    float result = fmaf(x, y, z);
+
+    if (!_isnanf(result))
+    {
+        Fail("fmaf(%g, %g, %g) returned %10.9g when it should have returned %10.9g",
+             x, y, z, result, PAL_NAN);
+    }
+}
+
+/**
+ * main
+ * 
+ * executable entry point
+ */
+int __cdecl main(int argc, char **argv)
+{
+    struct test tests[] = 
+    {
+        /* x                y                z                expected            variance */
+        {  PAL_NEGINF,      PAL_NEGINF,      PAL_NEGINF,      PAL_NEGINF,         0 },
+        { -1e38,            2,               1e38,           -1e38,               0 },
+        {  1e38,            2,              -1e38,            1e38,               0 },
+        {  PAL_POSINF,      PAL_POSINF,      PAL_POSINF,      PAL_POSINF,         0 },
+    };
+
+    if (PAL_Initialize(argc, argv) != 0)
+    {
+        return FAIL;
+    }
+
+    for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
+    {
+        validate(tests[i].x, tests[i].y, tests[i].z, tests[i].expected, tests[i].variance);
+    }
+
+    // Returns NaN if x or y is infinite, the other is zero, and z is NaN
+    validate_isnan(PAL_NEGINF, 0, PAL_NAN);
+    validate_isnan(PAL_POSINF, 0, PAL_NAN);
+    validate_isnan(0, PAL_NEGINF, PAL_NAN);
+    validate_isnan(0, PAL_POSINF, PAL_NAN);
+
+    // Returns NaN if x or y is infinite, the other is zero, and z is not-NaN
+    validate_isnan(PAL_POSINF, 0, PAL_NEGINF);
+    validate_isnan(PAL_NEGINF, 0, PAL_NEGINF);
+    validate_isnan(0, PAL_POSINF, PAL_NEGINF);
+    validate_isnan(0, PAL_NEGINF, PAL_NEGINF);
+    
+    validate_isnan(PAL_POSINF, 0, 0);
+    validate_isnan(PAL_NEGINF, 0, 0);
+    validate_isnan(0, PAL_POSINF, 0);
+    validate_isnan(0, PAL_NEGINF, 0);
+
+    validate_isnan(PAL_POSINF, 0, PAL_POSINF);
+    validate_isnan(PAL_NEGINF, 0, PAL_POSINF);
+    validate_isnan(0, PAL_POSINF, PAL_POSINF);
+    validate_isnan(0, PAL_NEGINF, PAL_POSINF);
+
+    // Returns NaN if (x * y) is infinite, and z is an infinite of the opposite sign
+    validate_isnan(PAL_POSINF, PAL_POSINF, PAL_NEGINF);
+    validate_isnan(PAL_NEGINF, PAL_NEGINF, PAL_POSINF);
+    validate_isnan(PAL_POSINF, PAL_NEGINF, PAL_POSINF);
+    validate_isnan(PAL_NEGINF, PAL_POSINF, PAL_POSINF);
+
+    validate_isnan(PAL_POSINF, 1, PAL_NEGINF);
+    validate_isnan(PAL_NEGINF, 1, PAL_POSINF);
+    validate_isnan(PAL_POSINF, 1, PAL_POSINF);
+    validate_isnan(PAL_NEGINF, 1, PAL_POSINF);
+
+    validate_isnan(1, PAL_POSINF, PAL_NEGINF);
+    validate_isnan(1, PAL_NEGINF, PAL_POSINF);
+    validate_isnan(1, PAL_NEGINF, PAL_POSINF);
+    validate_isnan(1, PAL_POSINF, PAL_POSINF);
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/c_runtime/fmaf/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/fmaf/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..8ca9fb7
--- /dev/null
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = C Runtime
+Function = fmaf
+Name = Call fmaf with some std input/output.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Call the fmaf function with various num/expfonent pairs
+= that should produce std answers.
+
+
+
+
diff --git a/src/pal/tests/palsuite/c_runtime/ilogb/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/ilogb/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e1ef7f
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/c_runtime/ilogb/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/ilogb/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8df6908
--- /dev/null
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+  test1.cpp
+)
+
+add_executable(paltest_ilogb_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_ilogb_test1 coreclrpal)
+
+target_link_libraries(paltest_ilogb_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/c_runtime/ilogb/test1/test1.cpp b/src/pal/tests/palsuite/c_runtime/ilogb/test1/test1.cpp
new file mode 100644 (file)
index 0000000..5df6fcf
--- /dev/null
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source:  test1.c
+**
+** Purpose: Tests that ilogb returns correct values.
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+#define PAL_NAN     sqrt(-1.0)
+#define PAL_POSINF -log(0.0)
+#define PAL_NEGINF  log(0.0)
+
+/**
+ * Helper test structure
+ */
+struct test
+{
+    double value;     /* value to test the function with */
+    int expected;     /* expected result */
+};
+
+/**
+ * validate
+ *
+ * test validation function
+ */
+void __cdecl validate(double value, int expected)
+{
+    int result = ilogb(value);
+
+    if (result != expected)
+    {
+        Fail("ilogb(%g) returned %10.10g when it should have returned %10.10g",
+             value, result, expected);
+    }
+}
+
+/**
+ * main
+ * 
+ * executable entry point
+ */
+int __cdecl main(int argc, char **argv)
+{
+    struct test tests[] = 
+    {
+        /* value                       expected */
+        {  PAL_NEGINF,                 0x80000000 },
+        {  0,                          0x80000000 },
+        {  PAL_POSINF,                 0x80000000 },
+        {  0.11331473229676087,       -3          },   // expected: -(pi)
+        {  0.15195522325791297,       -2          },   // expected: -(e)
+        {  0.20269956628651730,       -2          },   // expected: -(ln(10))
+        {  0.33662253682241906,       -1          },   // expected: -(pi / 2)
+        {  0.36787944117144232,       -1          },   // expected: -(log2(e))
+        {  0.37521422724648177,       -1          },   // expected: -(sqrt(2))
+        {  0.45742934732229695,       -1          },   // expected: -(2 / sqrt(pi))
+        {  0.5,                       -1          },   // expected: -(1)
+        {  0.58019181037172444,        0          },   // expected: -(pi / 4)
+        {  0.61254732653606592,        0          },   // expected: -(1 / sqrt(2))
+        {  0.61850313780157598,        0          },   // expected: -(ln(2))
+        {  0.64321824193300488,        0          },   // expected: -(2 / pi)
+        {  0.74005557395545179,        0          },   // expected: -(log10(e))
+        {  0.80200887896145195,        0          },   // expected: -(1 / pi)
+        {  1,                          0          },
+        {  1.2468689889006383,         0          },   // expected:  1 / pi
+        {  1.3512498725672678,         0          },   // expected:  log10(e)
+        {  1.5546822754821001,         0          },   // expected:  2 / pi
+        {  1.6168066722416747,         0          },   // expected:  ln(2)
+        {  1.6325269194381528,         0          },   // expected:  1 / sqrt(2)
+        {  1.7235679341273495,         0          },   // expected:  pi / 4
+        {  2,                          1          },
+        {  2.1861299583286618,         1          },   // expected:  2 / sqrt(pi)
+        {  2.6651441426902252,         1          },   // expected:  sqrt(2)
+        {  2.7182818284590452,         1          },   // expected:  log2(e)             value: e
+        {  2.9706864235520193,         1          },   // expected:  pi / 2
+        {  4.9334096679145963,         2          },   // expected:  ln(10)
+        {  6.5808859910179210,         2          },   // expected:  e
+        {  8.8249778270762876,         3          },   // expected:  pi
+        {  PAL_NAN,                    2147483647 },
+    };
+
+    if (PAL_Initialize(argc, argv) != 0)
+    {
+        return FAIL;
+    }
+
+    for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
+    {
+        validate(tests[i].value, tests[i].expected);
+    }
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/c_runtime/ilogb/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/ilogb/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..05549db
--- /dev/null
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = C Runtime
+Function = ilogb
+Name = Call ilogb with some std input/output.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Call the ilogb function with various num/exponent pairs
+= that should produce std answers.
+
+
+
+
diff --git a/src/pal/tests/palsuite/c_runtime/ilogbf/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/ilogbf/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e1ef7f
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/c_runtime/ilogbf/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/ilogbf/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b478f69
--- /dev/null
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+  test1.c
+)
+
+add_executable(paltest_ilogbf_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_ilogbf_test1 coreclrpal)
+
+target_link_libraries(paltest_ilogbf_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/c_runtime/ilogbf/test1/test1.c b/src/pal/tests/palsuite/c_runtime/ilogbf/test1/test1.c
new file mode 100644 (file)
index 0000000..2b97e44
--- /dev/null
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source:  test1.c
+**
+** Purpose: Tests that ilogbf returns correct values.
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+#define PAL_NAN     sqrtf(-1.0f)
+#define PAL_POSINF -logf(0.0f)
+#define PAL_NEGINF  logf(0.0f)
+
+/**
+ * Helper test structure
+ */
+struct test
+{
+    float value;     /* value to test the function with */
+    int expected;    /* expected result */
+};
+
+/**
+ * validate
+ *
+ * test validation function
+ */
+void __cdecl validate(float value, int expected)
+{
+    float result = ilogbf(value);
+
+    if (result != expected)
+    {
+        Fail("ilogbf(%g) returned %10.10g when it should have returned %10.10g",
+             value, result, expected);
+    }
+}
+
+/**
+ * main
+ * 
+ * executable entry point
+ */
+int __cdecl main(int argc, char **argv)
+{
+    struct test tests[] = 
+    {
+        /* value                expected */
+        {  PAL_NEGINF,          0x80000000 },
+        {  0,                   0x80000000 },
+        {  PAL_POSINF,          0x80000000 },
+        {  0.113314732f,       -3          },   // expected: -(pi)
+        {  0.151955223f,       -2          },   // expected: -(e)
+        {  0.202699566f,       -2          },   // expected: -(ln(10))
+        {  0.336622537f,       -1          },   // expected: -(pi / 2)
+        {  0.367879441f,       -1          },   // expected: -(log2(e))
+        {  0.375214227f,       -1          },   // expected: -(sqrt(2))
+        {  0.457429347f,       -1          },   // expected: -(2 / sqrt(pi))
+        {  0.5f,               -1          },   // expected: -(1)
+        {  0.580191810f,        0          },   // expected: -(pi / 4)
+        {  0.612547327f,        0          },   // expected: -(1 / sqrt(2))
+        {  0.618503138f,        0          },   // expected: -(ln(2))
+        {  0.643218242f,        0          },   // expected: -(2 / pi)
+        {  0.740055574f,        0          },   // expected: -(log10(e))
+        {  0.802008879f,        0          },   // expected: -(1 / pi)
+        {  1,                   0          },
+        {  1.24686899f,         0          },   // expected:  1 / pi
+        {  1.35124987f,         0          },   // expected:  log10(e)
+        {  1.55468228f,         0          },   // expected:  2 / pi
+        {  1.61680667f,         0          },   // expected:  ln(2)
+        {  1.63252692f,         0          },   // expected:  1 / sqrt(2)
+        {  1.72356793f,         0          },   // expected:  pi / 4
+        {  2,                   1          },
+        {  2.18612996f,         1          },   // expected:  2 / sqrt(pi)
+        {  2.66514414f,         1          },   // expected:  sqrt(2)
+        {  2.71828183f,         1          },   // expected:  log2(e)             value: e
+        {  2.97068642f,         1          },   // expected:  pi / 2
+        {  4.93340967f,         2          },   // expected:  ln(10)
+        {  6.58088599f,         2          },   // expected:  e
+        {  8.82497783f,         3          },   // expected:  pi
+        {  PAL_NAN,             2147483647 },
+    };
+
+    if (PAL_Initialize(argc, argv) != 0)
+    {
+        return FAIL;
+    }
+
+    for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
+    {
+        validate(tests[i].value, tests[i].expected);
+    }
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/c_runtime/ilogbf/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/ilogbf/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..8337bba
--- /dev/null
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = C Runtime
+Function = ilogbf
+Name = Call ilogbf with some std input/output.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Call the ilogbf function with various num/expfonent pairs
+= that should produce std answers.
+
+
+
+
diff --git a/src/pal/tests/palsuite/c_runtime/log2/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/log2/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e1ef7f
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/c_runtime/log2/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/log2/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ea0c178
--- /dev/null
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+  test1.cpp
+)
+
+add_executable(paltest_log2_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_log2_test1 coreclrpal)
+
+target_link_libraries(paltest_log2_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/c_runtime/log2/test1/test1.cpp b/src/pal/tests/palsuite/c_runtime/log2/test1/test1.cpp
new file mode 100644 (file)
index 0000000..c7900a0
--- /dev/null
@@ -0,0 +1,139 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source:  test1.c
+**
+** Purpose: Tests that log2 returns correct values.
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+// binary64 (double) has a machine epsilon of 2^-52 (approx. 2.22e-16). However, this 
+// is slightly too accurate when writing tests meant to run against libm implementations
+// for various platforms. 2^-50 (approx. 8.88e-16) seems to be as accurate as we can get.
+//
+// The tests themselves will take PAL_EPSILON and adjust it according to the expected result
+// so that the delta used for comparison will compare the most significant digits and ignore
+// any digits that are outside the double precision range (15-17 digits).
+
+// For example, a test with an expect result in the format of 0.xxxxxxxxxxxxxxxxx will use
+// PAL_EPSILON for the variance, while an expected result in the format of 0.0xxxxxxxxxxxxxxxxx
+// will use PAL_EPSILON / 10 and and expected result in the format of x.xxxxxxxxxxxxxxxx will
+// use PAL_EPSILON * 10.
+#define PAL_EPSILON 8.8817841970012523e-16
+
+#define PAL_NAN     sqrt(-1.0)
+#define PAL_POSINF -log(0.0)
+#define PAL_NEGINF  log(0.0)
+
+/**
+ * Helper test structure
+ */
+struct test
+{
+    double value;     /* value to test the function with */
+    double expected;  /* expected result */
+    double variance;  /* maximum delta between the expected and actual result */
+};
+
+/**
+ * validate
+ *
+ * test validation function
+ */
+void __cdecl validate(double value, double expected, double variance)
+{
+    double result = log2(value);
+
+    /*
+     * The test is valid when the difference between result
+     * and expected is less than or equal to variance
+     */
+    double delta = fabs(result - expected);
+
+    if (delta > variance)
+    {
+        Fail("log2(%g) returned %20.17g when it should have returned %20.17g",
+             value, result, expected);
+    }
+}
+
+/**
+ * validate
+ *
+ * test validation function for values returning NaN
+ */
+void __cdecl validate_isnan(double value)
+{
+    double result = log2(value);
+
+    if (!_isnan(result))
+    {
+        Fail("log2(%g) returned %20.17g when it should have returned %20.17g",
+             value, result, PAL_NAN);
+    }
+}
+
+/**
+ * main
+ * 
+ * executable entry point
+ */
+int __cdecl main(int argc, char **argv)
+{
+    struct test tests[] = 
+    {
+        /* value                       expected               variance */
+        {  0,                          PAL_NEGINF,            0 },
+        {  0.11331473229676087,       -3.1415926535897932,    PAL_EPSILON * 10 },   // expected: -(pi)
+        {  0.15195522325791297,       -2.7182818284590452,    PAL_EPSILON * 10 },   // expected: -(e)
+        {  0.20269956628651730,       -2.3025850929940457,    PAL_EPSILON * 10 },   // expected: -(ln(10))
+        {  0.33662253682241906,       -1.5707963267948966,    PAL_EPSILON * 10 },   // expected: -(pi / 2)
+        {  0.36787944117144232,       -1.4426950408889634,    PAL_EPSILON * 10 },   // expected: -(log2(e))
+        {  0.37521422724648177,       -1.4142135623730950,    PAL_EPSILON * 10 },   // expected: -(sqrt(2))
+        {  0.45742934732229695,       -1.1283791670955126,    PAL_EPSILON * 10 },   // expected: -(2 / sqrt(pi))
+        {  0.5,                       -1,                     PAL_EPSILON * 10 },   // expected: -(1)
+        {  0.58019181037172444,       -0.78539816339744831,   PAL_EPSILON },        // expected: -(pi / 4)
+        {  0.61254732653606592,       -0.70710678118654752,   PAL_EPSILON },        // expected: -(1 / sqrt(2))
+        {  0.61850313780157598,       -0.69314718055994531,   PAL_EPSILON },        // expected: -(ln(2))
+        {  0.64321824193300488,       -0.63661977236758134,   PAL_EPSILON },        // expected: -(2 / pi)
+        {  0.74005557395545179,       -0.43429448190325183,   PAL_EPSILON },        // expected: -(log10(e))
+        {  0.80200887896145195,       -0.31830988618379067,   PAL_EPSILON },        // expected: -(1 / pi)
+        {  1,                          0,                     PAL_EPSILON },
+        {  1.2468689889006383,         0.31830988618379067,   PAL_EPSILON },        // expected:  1 / pi
+        {  1.3512498725672678,         0.43429448190325183,   PAL_EPSILON },        // expected:  log10(e)
+        {  1.5546822754821001,         0.63661977236758134,   PAL_EPSILON },        // expected:  2 / pi
+        {  1.6168066722416747,         0.69314718055994531,   PAL_EPSILON },        // expected:  ln(2)
+        {  1.6325269194381528,         0.70710678118654752,   PAL_EPSILON },        // expected:  1 / sqrt(2)
+        {  1.7235679341273495,         0.78539816339744831,   PAL_EPSILON },        // expected:  pi / 4
+        {  2,                          1,                     PAL_EPSILON * 10 },
+        {  2.1861299583286618,         1.1283791670955126,    PAL_EPSILON * 10 },   // expected:  2 / sqrt(pi)
+        {  2.6651441426902252,         1.4142135623730950,    PAL_EPSILON * 10 },   // expected:  sqrt(2)
+        {  2.7182818284590452,         1.4426950408889634,    PAL_EPSILON * 10 },   // expected:  log2(e)             value: e
+        {  2.9706864235520193,         1.5707963267948966,    PAL_EPSILON * 10 },   // expected:  pi / 2
+        {  4.9334096679145963,         2.3025850929940457,    PAL_EPSILON * 10 },   // expected:  ln(10)
+        {  6.5808859910179210,         2.7182818284590452,    PAL_EPSILON * 10 },   // expected:  e
+        {  8.8249778270762876,         3.1415926535897932,    PAL_EPSILON * 10 },   // expected:  pi
+        {  PAL_POSINF,                 PAL_POSINF,            0 },
+    };
+
+    if (PAL_Initialize(argc, argv) != 0)
+    {
+        return FAIL;
+    }
+
+    for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
+    {
+        validate(tests[i].x, tests[i].y, tests[i].z, tests[i].expected, tests[i].variance);
+    }
+
+    validate_isnan(PAL_NEGINF);
+    validate_isnan(PAL_NAN);
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/c_runtime/log2/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/log2/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..ef6268e
--- /dev/null
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = C Runtime
+Function = log2
+Name = Call log2 with some std input/output.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Call the log2 function with various num/exponent pairs
+= that should produce std answers.
+
+
+
+
diff --git a/src/pal/tests/palsuite/c_runtime/log2f/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/log2f/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e1ef7f
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/c_runtime/log2f/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/log2f/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..bee2084
--- /dev/null
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+  test1.c
+)
+
+add_executable(paltest_log2f_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_log2f_test1 coreclrpal)
+
+target_link_libraries(paltest_log2f_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/c_runtime/log2f/test1/test1.c b/src/pal/tests/palsuite/c_runtime/log2f/test1/test1.c
new file mode 100644 (file)
index 0000000..5231aa5
--- /dev/null
@@ -0,0 +1,138 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source:  test1.c
+**
+** Purpose: Tests that log2f returns correct values.
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+// binary32 (float) has a machine epsilon of 2^-23 (approx. 1.19e-07). However, this 
+// is slightly too accurate when writing tests meant to run against libm implementations
+// for various platforms. 2^-21 (approx. 4.76e-07) seems to be as accurate as we can get.
+//
+// The tests themselves will take PAL_EPSILON and adjust it according to the expected result
+// so that the delta used for comparison will compare the most significant digits and ignore
+// any digits that are outside the double precision range (6-9 digits).
+
+// For example, a test with an expect result in the format of 0.xxxxxxxxx will use PAL_EPSILON
+// for the variance, while an expected result in the format of 0.0xxxxxxxxx will use
+// PAL_EPSILON / 10 and and expected result in the format of x.xxxxxx will use PAL_EPSILON * 10.
+#define PAL_EPSILON 4.76837158e-07
+
+#define PAL_NAN     sqrtf(-1.0f)
+#define PAL_POSINF -logf(0.0f)
+#define PAL_NEGINF  logf(0.0f)
+
+/**
+ * Helper test structure
+ */
+struct test
+{
+    float value;     /* value to test the function with */
+    float expected;  /* expected result */
+    float variance;  /* maximum delta between the expected and actual result */
+};
+
+/**
+ * validate
+ *
+ * test validation function
+ */
+void __cdecl validate(float value, float expected, float variance)
+{
+    float result = log2f(value);
+
+    /*
+     * The test is valid when the difference between result
+     * and expected is less than or equal to variance
+     */
+    float delta = fabsf(result - expected);
+
+    if (delta > variance)
+    {
+        Fail("log2f(%g) returned %10.9g when it should have returned %10.9g",
+             value, result, expected);
+    }
+}
+
+/**
+ * validate
+ *
+ * test validation function for values returning NaN
+ */
+void __cdecl validate_isnan(float value)
+{
+    float result = log2f(value);
+
+    if (!_isnanf(result))
+    {
+        Fail("log2f(%g) returned %10.9g when it should have returned %10.9g",
+             value, result, PAL_NAN);
+    }
+}
+
+/**
+ * main
+ * 
+ * executable entry point
+ */
+int __cdecl main(int argc, char **argv)
+{
+    struct test tests[] = 
+    {
+        /* value                expected        variance */
+        {  0,                   PAL_NEGINF,     0 },
+        {  0.113314732f,       -3.14159265f,    PAL_EPSILON * 10 },   // expected: -(pi)
+        {  0.151955223f,       -2.71828183f,    PAL_EPSILON * 10 },   // expected: -(e)
+        {  0.202699566f,       -2.30258509f,    PAL_EPSILON * 10 },   // expected: -(ln(10))
+        {  0.336622537f,       -1.57079633f,    PAL_EPSILON * 10 },   // expected: -(pi / 2)
+        {  0.367879441f,       -1.44269504f,    PAL_EPSILON * 10 },   // expected: -(logf2(e))
+        {  0.375214227f,       -1.41421356f,    PAL_EPSILON * 10 },   // expected: -(sqrtf(2))
+        {  0.457429347f,       -1.12837917f,    PAL_EPSILON * 10 },   // expected: -(2 / sqrtf(pi))
+        {  0.5f,               -1,              PAL_EPSILON * 10 },   // expected: -(1)
+        {  0.580191810f,       -0.785398163f,   PAL_EPSILON },        // expected: -(pi / 4)
+        {  0.612547327f,       -0.707106781f,   PAL_EPSILON },        // expected: -(1 / sqrtf(2))
+        {  0.618503138f,       -0.693147181f,   PAL_EPSILON },        // expected: -(ln(2))
+        {  0.643218242f,       -0.636619772f,   PAL_EPSILON },        // expected: -(2 / pi)
+        {  0.740055574f,       -0.434294482f,   PAL_EPSILON },        // expected: -(log10f(e))
+        {  0.802008879f,       -0.318309886f,   PAL_EPSILON },        // expected: -(1 / pi)
+        {  1,                   0,              PAL_EPSILON },
+        {  1.24686899f,         0.318309886f,   PAL_EPSILON },        // expected:  1 / pi
+        {  1.35124987f,         0.434294482f,   PAL_EPSILON },        // expected:  log10f(e)           value: e
+        {  1.55468228f,         0.636619772f,   PAL_EPSILON },        // expected:  2 / pi
+        {  1.61680667f,         0.693147181f,   PAL_EPSILON },        // expected:  ln(2)
+        {  1.63252692f,         0.707106781f,   PAL_EPSILON },        // expected:  1 / sqrtf(2)
+        {  1.72356793f,         0.785398163f,   PAL_EPSILON },        // expected:  pi / 4
+        {  2,                   1,              PAL_EPSILON * 10 },
+        {  2.18612996f,         1.12837917f,    PAL_EPSILON * 10 },   // expected:  2 / sqrtf(pi)
+        {  2.66514414f,         1.41421356f,    PAL_EPSILON * 10 },   // expected:  sqrtf(2)
+        {  2.71828183f,         1.44269504f,    PAL_EPSILON * 10 },   // expected:  logf2(e)
+        {  2.97068642f,         1.57079633f,    PAL_EPSILON * 10 },   // expected:  pi / 2
+        {  4.93340967f,         2.30258509f,    PAL_EPSILON * 10 },   // expected:  ln(10)
+        {  6.58088599f,         2.71828183f,    PAL_EPSILON * 10 },   // expected:  e
+        {  8.82497783f,         3.14159265f,    PAL_EPSILON * 10 },   // expected:  pi
+        {  PAL_POSINF,          PAL_POSINF,     0 },
+    };
+
+    if (PAL_Initialize(argc, argv) != 0)
+    {
+        return FAIL;
+    }
+
+    for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
+    {
+        validate(tests[i].x, tests[i].y, tests[i].z, tests[i].expected, tests[i].variance);
+    }
+
+    validate_isnan(PAL_NEGINF);
+    validate_isnan(PAL_NAN);
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/c_runtime/log2f/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/log2f/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..7627c82
--- /dev/null
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = C Runtime
+Function = log2f
+Name = Call log2f with some std input/output.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Call the log2f function with various num/expfonent pairs
+= that should produce std answers.
+
+
+
+
diff --git a/src/pal/tests/palsuite/c_runtime/scalbn/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/scalbn/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e1ef7f
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/c_runtime/scalbn/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/scalbn/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..083cd11
--- /dev/null
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+  test1.cpp
+)
+
+add_executable(paltest_scalbn_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_scalbn_test1 coreclrpal)
+
+target_link_libraries(paltest_scalbn_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/c_runtime/scalbn/test1/test1.cpp b/src/pal/tests/palsuite/c_runtime/scalbn/test1/test1.cpp
new file mode 100644 (file)
index 0000000..9507cc4
--- /dev/null
@@ -0,0 +1,140 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source:  test1.c
+**
+** Purpose: Tests that scalbn returns correct values.
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+// binary64 (double) has a machine epsilon of 2^-52 (approx. 2.22e-16). However, this 
+// is slightly too accurate when writing tests meant to run against libm implementations
+// for various platforms. 2^-50 (approx. 8.88e-16) seems to be as accurate as we can get.
+//
+// The tests themselves will take PAL_EPSILON and adjust it according to the expected result
+// so that the delta used for comparison will compare the most significant digits and ignore
+// any digits that are outside the double precision range (15-17 digits).
+
+// For example, a test with an expect result in the format of 0.xxxxxxxxxxxxxxxxx will use
+// PAL_EPSILON for the variance, while an expected result in the format of 0.0xxxxxxxxxxxxxxxxx
+// will use PAL_EPSILON / 10 and and expected result in the format of x.xxxxxxxxxxxxxxxx will
+// use PAL_EPSILON * 10.
+#define PAL_EPSILON 8.8817841970012523e-16
+
+#define PAL_NAN     sqrt(-1.0)
+#define PAL_POSINF -log(0.0)
+#define PAL_NEGINF  log(0.0)
+
+/**
+ * Helper test structure
+ */
+struct test
+{
+    double value;     /* value to test the function with */
+    int   exponent;  /* exponent to test the function with */
+    double expected;  /* expected result */
+    double variance;  /* maximum delta between the expected and actual result */
+};
+
+/**
+ * validate
+ *
+ * test validation function
+ */
+void __cdecl validate(double value, int exponent, double expected, double variance)
+{
+    double result = scalbn(value, exponent);
+
+    /*
+     * The test is valid when the difference between result
+     * and expected is less than or equal to variance
+     */
+    double delta = fabs(result - expected);
+
+    if (delta > variance)
+    {
+        Fail("scalbn(%g, %d) returned %20.17g when it should have returned %20.17g\n",
+             value, exponent, result, expected);
+    }
+}
+
+/**
+ * validate
+ *
+ * test validation function for values returning NaN
+ */
+void __cdecl validate_isnan(double value, int exponent)
+{
+    double result = scalbn(value, exponent);
+
+    if (!_isnan(result))
+    {
+        Fail("scalbn(%g, %d) returned %20.17g when it should have returned %20.17g\n",
+             value, exponent, result, PAL_NAN);
+    }
+}
+
+/**
+ * main
+ * 
+ * executable entry point
+ */
+int __cdecl main(int argc, char **argv)
+{
+    struct test tests[] = 
+    {
+        /* value                       exponent           expected               variance */
+        {  PAL_NEGINF,                 0x80000000,        PAL_NEGINF,            0 },
+        {  0,                          0x80000000,        0,                     0 },
+        {  0.11331473229676087,       -3,                 0.014164341537095108,  PAL_EPSILON / 10 },
+        {  0.15195522325791297,       -2,                 0.037988805814478242,  PAL_EPSILON / 10 },
+        {  0.20269956628651730,       -2,                 0.050674891571629327,  PAL_EPSILON / 10 },
+        {  0.33662253682241906,       -1,                 0.16831126841120952,   PAL_EPSILON },
+        {  0.36787944117144232,       -1,                 0.18393972058572117,   PAL_EPSILON },
+        {  0.37521422724648177,       -1,                 0.1876071136232409,    PAL_EPSILON },
+        {  0.45742934732229695,       -1,                 0.22871467366114848,   PAL_EPSILON },
+        {  0.5,                       -1,                 0.25,                  PAL_EPSILON },
+        {  0.58019181037172444,        0,                 0.5801918103717244,    PAL_EPSILON },
+        {  0.61254732653606592,        0,                 0.61254732653606592,   PAL_EPSILON },
+        {  0.61850313780157598,        0,                 0.61850313780157595,   PAL_EPSILON },
+        {  0.64321824193300488,        0,                 0.64321824193300492,   PAL_EPSILON },
+        {  0.74005557395545179,        0,                 0.74005557395545174,   PAL_EPSILON },
+        {  0.80200887896145195,        0,                 0.8020088789614519,    PAL_EPSILON },
+        {  1,                          0,                 1,                     PAL_EPSILON * 10 },
+        {  1.2468689889006383,         0,                 1.2468689889006384,    PAL_EPSILON * 10 },
+        {  1.3512498725672678,         0,                 1.3512498725672677,    PAL_EPSILON * 10 },
+        {  1.5546822754821001,         0,                 1.5546822754821001,    PAL_EPSILON * 10 },
+        {  1.6168066722416747,         0,                 1.6168066722416747,    PAL_EPSILON * 10 },
+        {  1.6325269194381528,         0,                 1.6325269194381529,    PAL_EPSILON * 10 },
+        {  1.7235679341273495,         0,                 1.7235679341273495,    PAL_EPSILON * 10 },
+        {  2,                          1,                 4,                     PAL_EPSILON * 10 },
+        {  2.1861299583286618,         1,                 4.3722599166573239,    PAL_EPSILON * 10 },
+        {  2.6651441426902252,         1,                 5.3302882853804503,    PAL_EPSILON * 10 },
+        {  2.7182818284590452,         1,                 5.4365636569180902,    PAL_EPSILON * 10 },
+        {  2.9706864235520193,         1,                 5.9413728471040388,    PAL_EPSILON * 10 },
+        {  4.9334096679145963,         2,                 19.733638671658387,    PAL_EPSILON * 100 },
+        {  6.5808859910179210,         2,                 26.323543964071686,    PAL_EPSILON * 100 },
+        {  8.8249778270762876,         3,                 70.599822616610297,    PAL_EPSILON * 100 },
+        {  PAL_POSINF,                 0x80000000,        PAL_POSINF,            0 },
+    };
+
+    if (PAL_Initialize(argc, argv) != 0)
+    {
+        return FAIL;
+    }
+
+    for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
+    {
+        validate(tests[i].value, tests[i].exponent, tests[i].expected, tests[i].variance);
+    }
+
+    validate_isnan(PAL_NAN, 2147483647);
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/c_runtime/scalbn/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/scalbn/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..ce98062
--- /dev/null
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = C Runtime
+Function = scalbn
+Name = Call scalbn with some std input/output.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Call the scalbn function with various num/exponent pairs
+= that should produce std answers.
+
+
+
+
diff --git a/src/pal/tests/palsuite/c_runtime/scalbnf/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/scalbnf/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e1ef7f
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/c_runtime/scalbnf/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/scalbnf/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e14b54a
--- /dev/null
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+  test1.c
+)
+
+add_executable(paltest_scalbnf_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_scalbnf_test1 coreclrpal)
+
+target_link_libraries(paltest_scalbnf_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/c_runtime/scalbnf/test1/test1.c b/src/pal/tests/palsuite/c_runtime/scalbnf/test1/test1.c
new file mode 100644 (file)
index 0000000..88f00a2
--- /dev/null
@@ -0,0 +1,139 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source:  test1.c
+**
+** Purpose: Tests that scalbnf returns correct values.
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+// binary32 (float) has a machine epsilon of 2^-23 (approx. 1.19e-07). However, this 
+// is slightly too accurate when writing tests meant to run against libm implementations
+// for various platforms. 2^-21 (approx. 4.76e-07) seems to be as accurate as we can get.
+//
+// The tests themselves will take PAL_EPSILON and adjust it according to the expected result
+// so that the delta used for comparison will compare the most significant digits and ignore
+// any digits that are outside the double precision range (6-9 digits).
+
+// For example, a test with an expect result in the format of 0.xxxxxxxxx will use PAL_EPSILON
+// for the variance, while an expected result in the format of 0.0xxxxxxxxx will use
+// PAL_EPSILON / 10 and and expected result in the format of x.xxxxxx will use PAL_EPSILON * 10.
+#define PAL_EPSILON 4.76837158e-07
+
+#define PAL_NAN     sqrtf(-1.0f)
+#define PAL_POSINF -logf(0.0f)
+#define PAL_NEGINF  logf(0.0f)
+
+/**
+ * Helper test structure
+ */
+struct test
+{
+    float value;     /* value to test the function with */
+    int   exponent;  /* exponent to test the function with */
+    float expected;  /* expected result */
+    float variance;  /* maximum delta between the expected and actual result */
+};
+
+/**
+ * validate
+ *
+ * test validation function
+ */
+void __cdecl validate(float value, int exponent, float expected, float variance)
+{
+    float result = scalbnf(value, exponent);
+
+    /*
+     * The test is valid when the difference between result
+     * and expected is less than or equal to variance
+     */
+    float delta = fabsf(result - expected);
+
+    if (delta > variance)
+    {
+        Fail("scalbnf(%g, %g) returned %10.9g when it should have returned %10.9g",
+             value, exponent, result, expected);
+    }
+}
+
+/**
+ * validate
+ *
+ * test validation function for values returning NaN
+ */
+void __cdecl validate_isnan(float value, int exponent)
+{
+    float result = scalbnf(value, exponent);
+
+    if (!_isnanf(result))
+    {
+        Fail("scalbnf(%g, %g) returned %10.9g when it should have returned %10.9g",
+             value, exponent, result, PAL_NAN);
+    }
+}
+
+/**
+ * main
+ * 
+ * executable entry point
+ */
+int __cdecl main(int argc, char **argv)
+{
+    struct test tests[] = 
+    {
+        /* value                exponent           expected        variance */
+        {  PAL_NEGINF,          0x80000000,        PAL_NEGINF,     0 },
+        {  0,                   0x80000000,        0,              0 },
+        {  0.113314732f,       -3,                 0.0141643415f,  PAL_EPSILON / 10 },
+        {  0.151955223f,       -2,                 0.0379888058f,  PAL_EPSILON / 10 },
+        {  0.202699566f,       -2,                 0.0506748916f,  PAL_EPSILON / 10 },
+        {  0.336622537f,       -1,                 0.168311268f,   PAL_EPSILON },
+        {  0.367879441f,       -1,                 0.183939721f,   PAL_EPSILON },
+        {  0.375214227f,       -1,                 0.187607114f,   PAL_EPSILON },
+        {  0.457429347f,       -1,                 0.228714674f,   PAL_EPSILON },
+        {  0.5f,               -1,                 0.25f,          PAL_EPSILON },
+        {  0.580191810f,        0,                 0.580191810f,   PAL_EPSILON },
+        {  0.612547327f,        0,                 0.612547327f,   PAL_EPSILON },
+        {  0.618503138f,        0,                 0.618503138f,   PAL_EPSILON },
+        {  0.643218242f,        0,                 0.643218242f,   PAL_EPSILON },
+        {  0.740055574f,        0,                 0.740055574f,   PAL_EPSILON },
+        {  0.802008879f,        0,                 0.802008879f,   PAL_EPSILON },
+        {  1,                   0,                 1,              PAL_EPSILON * 10 },
+        {  1.24686899f,         0,                 1.24686899f,    PAL_EPSILON * 10 },
+        {  1.35124987f,         0,                 1.35124987f,    PAL_EPSILON * 10 },
+        {  1.55468228f,         0,                 1.55468228f,    PAL_EPSILON * 10 },
+        {  1.61680667f,         0,                 1.61680667f,    PAL_EPSILON * 10 },
+        {  1.63252692f,         0,                 1.63252692f,    PAL_EPSILON * 10 },
+        {  1.72356793f,         0,                 1.72356793f,    PAL_EPSILON * 10 },
+        {  2,                   1,                 4,              PAL_EPSILON * 10 },
+        {  2.18612996f,         1,                 4.37225992f,    PAL_EPSILON * 10 },
+        {  2.66514414f,         1,                 5.33028829f,    PAL_EPSILON * 10 },
+        {  2.71828183f,         1,                 5.43656366f,    PAL_EPSILON * 10 },
+        {  2.97068642f,         1,                 5.94137285f,    PAL_EPSILON * 10 },
+        {  4.93340967f,         2,                 19.7336387f,    PAL_EPSILON * 100 },
+        {  6.58088599f,         2,                 26.3235440f,    PAL_EPSILON * 100 },
+        {  8.82497783f,         3,                 70.5998226f,    PAL_EPSILON * 100 },
+        {  PAL_POSINF,          0x80000000,        PAL_POSINF,     0 },
+    };
+
+    if (PAL_Initialize(argc, argv) != 0)
+    {
+        return FAIL;
+    }
+
+    for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
+    {
+        validate(tests[i].value, tests[i].exponent, tests[i].expected, tests[i].variance);
+    }
+
+    validate_isnan(PAL_NAN, 2147483647);
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/c_runtime/scalbnf/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/scalbnf/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..728fafa
--- /dev/null
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = C Runtime
+Function = scalbnf
+Name = Call scalbnf with some std input/output.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Call the scalbnf function with various num/expfonent pairs
+= that should produce std answers.
+
+
+
+
index e168886..31d3661 100644 (file)
@@ -624,10 +624,14 @@ FCFuncStart(gMathFuncs)
     FCIntrinsic("Exp", COMDouble::Exp, CORINFO_INTRINSIC_Exp)
     FCIntrinsic("Floor", COMDouble::Floor, CORINFO_INTRINSIC_Floor)
     FCFuncElement("FMod", COMDouble::FMod)
+    FCFuncElement("FusedMultiplyAdd", COMDouble::FusedMultiplyAdd)
+    FCFuncElement("IlogB", COMDouble::IlogB)
     FCFuncElement("Log", COMDouble::Log)
+    FCFuncElement("Log2", COMDouble::Log2)
     FCIntrinsic("Log10", COMDouble::Log10, CORINFO_INTRINSIC_Log10)
     FCFuncElement("ModF", COMDouble::ModF)
     FCIntrinsic("Pow", COMDouble::Pow, CORINFO_INTRINSIC_Pow)
+    FCFuncElement("ScaleB", COMDouble::ScaleB)
     FCIntrinsic("Sin", COMDouble::Sin, CORINFO_INTRINSIC_Sin)
     FCIntrinsic("Sinh", COMDouble::Sinh, CORINFO_INTRINSIC_Sinh)
     FCIntrinsic("Sqrt", COMDouble::Sqrt, CORINFO_INTRINSIC_Sqrt)
@@ -650,10 +654,14 @@ FCFuncStart(gMathFFuncs)
     FCIntrinsic("Exp", COMSingle::Exp, CORINFO_INTRINSIC_Exp)
     FCIntrinsic("Floor", COMSingle::Floor, CORINFO_INTRINSIC_Floor)
     FCFuncElement("FMod", COMSingle::FMod)
+    FCFuncElement("FusedMultiplyAdd", COMSingle::FusedMultiplyAdd)
+    FCFuncElement("IlogB", COMSingle::IlogB)
     FCFuncElement("Log", COMSingle::Log)
+    FCFuncElement("Log2", COMSingle::Log2)
     FCIntrinsic("Log10", COMSingle::Log10, CORINFO_INTRINSIC_Log10)
     FCFuncElement("ModF", COMSingle::ModF)
     FCIntrinsic("Pow", COMSingle::Pow, CORINFO_INTRINSIC_Pow)
+    FCFuncElement("ScaleB", COMSingle::ScaleB)
     FCIntrinsic("Sin", COMSingle::Sin, CORINFO_INTRINSIC_Sin)
     FCIntrinsic("Sinh", COMSingle::Sinh, CORINFO_INTRINSIC_Sinh)
     FCIntrinsic("Sqrt", COMSingle::Sqrt, CORINFO_INTRINSIC_Sqrt)
index 1cf4241..9fb7ba0 100644 (file)
@@ -413,6 +413,7 @@ LPVOID __FCThrowArgument(LPVOID me, enum RuntimeExceptionKind reKind, LPCWSTR ar
 #define FCDECL3_IVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, a1, a3, a2)
 #define FCDECL3_IVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, a3, a1, a2)
 #define FCDECL3_VVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, a3, a2, a1)
+#define FCDECL3_VVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, int /* ECX */, a3, a2, a1)
 #define FCDECL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a4, a3)
 #define FCDECL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a5, a4, a3)
 #define FCDECL6(rettype, funcname, a1, a2, a3, a4, a5, a6) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a6, a5, a4, a3)
@@ -444,6 +445,7 @@ LPVOID __FCThrowArgument(LPVOID me, enum RuntimeExceptionKind reKind, LPCWSTR ar
 #define FCDECL3_IVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a3, a2)
 #define FCDECL3_IVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a3, a2)
 #define FCDECL3_VVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a2, a1, a3)
+#define FCDECL3_VVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a3, a2, a1)
 #define FCDECL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(a1, a2, a4, a3)
 #define FCDECL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(a1, a2, a5, a4, a3)
 #define FCDECL6(rettype, funcname, a1, a2, a3, a4, a5, a6) rettype F_CALL_CONV funcname(a1, a2, a6, a5, a4, a3)
@@ -499,6 +501,7 @@ LPVOID __FCThrowArgument(LPVOID me, enum RuntimeExceptionKind reKind, LPCWSTR ar
 #define FCDECL3_IVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3)
 #define FCDECL3_IVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3)
 #define FCDECL3_VVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3)
+#define FCDECL3_VVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3)
 #define FCDECL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(a1, a2, a3, a4)
 #define FCDECL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(a1, a2, a3, a4, a5)
 #define FCDECL6(rettype, funcname, a1, a2, a3, a4, a5, a6) rettype F_CALL_CONV funcname(a1, a2, a3, a4, a5, a6)
@@ -1009,6 +1012,7 @@ public:
 #define FCIMPL3_IVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, a1, a3, a2) { FCIMPL_PROLOG(funcname)
 #define FCIMPL3_IVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, a3, a1, a2) { FCIMPL_PROLOG(funcname)
 #define FCIMPL3_VVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, a3, a2, a1) {  FCIMPL_PROLOG(funcname)
+#define FCIMPL3_VVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, int /* ECX */, a3, a2, a1) {  FCIMPL_PROLOG(funcname)
 #define FCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a4, a3) { FCIMPL_PROLOG(funcname)
 #define FCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a5, a4, a3) { FCIMPL_PROLOG(funcname)
 #define FCIMPL6(rettype, funcname, a1, a2, a3, a4, a5, a6) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a6, a5, a4, a3) { FCIMPL_PROLOG(funcname)
@@ -1054,6 +1058,8 @@ public:
     rettype F_CALL_CONV funcname(a1, a3, a2) { FCIMPL_PROLOG(funcname)
 #define FCIMPL3_VVI(rettype, funcname, a1, a2, a3) FCSIGCHECK(funcname, #rettype "," "V" #a1 "," "V" #a2 "," #a3) \
     rettype F_CALL_CONV funcname(a2, a1, a3) { FCIMPL_PROLOG(funcname)
+#define FCIMPL3_VVV(rettype, funcname, a1, a2, a3) FCSIGCHECK(funcname, #rettype "," "V" #a1 "," "V" #a2 "," "V" #a3) \
+    rettype F_CALL_CONV funcname(a3, a2, a1) { FCIMPL_PROLOG(funcname)
 #define FCIMPL4(rettype, funcname, a1, a2, a3, a4) FCSIGCHECK(funcname, #rettype "," #a1 "," #a2 "," #a3 "," #a4) \
     rettype F_CALL_CONV funcname(a1, a2, a4, a3) { FCIMPL_PROLOG(funcname)
 #define FCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) FCSIGCHECK(funcname, #rettype "," #a1 "," #a2 "," #a3 "," #a4 "," #a5) \
@@ -1100,6 +1106,7 @@ public:
 #define FCIMPL3_VII(rettype, funcname, a1, a2, a3) rettype funcname(a1, a2, a3) {  FCIMPL_PROLOG(funcname)
 #define FCIMPL3_IVI(rettype, funcname, a1, a2, a3) rettype funcname(a1, a2, a3) {  FCIMPL_PROLOG(funcname)
 #define FCIMPL3_VVI(rettype, funcname, a1, a2, a3) rettype funcname(a1, a2, a3) {  FCIMPL_PROLOG(funcname)
+#define FCIMPL3_VVV(rettype, funcname, a1, a2, a3) rettype funcname(a1, a2, a3) {  FCIMPL_PROLOG(funcname)
 #define FCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype funcname(a1, a2, a3, a4) {  FCIMPL_PROLOG(funcname)
 #define FCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype funcname(a1, a2, a3, a4, a5) {  FCIMPL_PROLOG(funcname)
 #define FCIMPL6(rettype, funcname, a1, a2, a3, a4, a5, a6) rettype funcname(a1, a2, a3, a4, a5, a6) {  FCIMPL_PROLOG(funcname)