1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*=====================================================================
6**
7** Source: test1.c
8**
9** Purpose: Tests that fma returns correct values for a subset of values.
10** Tests with positive and negative values of x, y, and z to ensure
11** fmaf is returning correct results.
12**
13**===================================================================*/
14
15#include <palsuite.h>
16
17// binary64 (double) has a machine epsilon of 2^-52 (approx. 2.22e-16). However, this
18// is slightly too accurate when writing tests meant to run against libm implementations
19// for various platforms. 2^-50 (approx. 8.88e-16) seems to be as accurate as we can get.
20//
21// The tests themselves will take PAL_EPSILON and adjust it according to the expected result
22// so that the delta used for comparison will compare the most significant digits and ignore
23// any digits that are outside the double precision range (15-17 digits).
24
25// For example, a test with an expect result in the format of 0.xxxxxxxxxxxxxxxxx will use
26// PAL_EPSILON for the variance, while an expected result in the format of 0.0xxxxxxxxxxxxxxxxx
27// will use PAL_EPSILON / 10 and and expected result in the format of x.xxxxxxxxxxxxxxxx will
28// use PAL_EPSILON * 10.
29#define PAL_EPSILON 8.8817841970012523e-16
30
31#define PAL_NAN sqrt(-1.0)
32#define PAL_POSINF -log(0.0)
33#define PAL_NEGINF log(0.0)
34
35/**
36 * Helper test structure
37 */
38struct test
39{
40 double x; /* first component of the value to test the function with */
41 double y; /* second component of the value to test the function with */
42 double z; /* third component of the value to test the function with */
43 double expected; /* expected result */
44 double variance; /* maximum delta between the expected and actual result */
45};
46
47/**
48 * validate
49 *
50 * test validation function
51 */
52void __cdecl validate(double x, double y, double z, double expected, double variance)
53{
54 double result = fma(x, y, z);
55
56 /*
57 * The test is valid when the difference between result
58 * and expected is less than or equal to variance
59 */
60 double delta = fabs(result - expected);
61
62 if (delta > variance)
63 {
64 Fail("fma(%g, %g, %g) returned %20.17g when it should have returned %20.17g",
65 x, y, z, result, expected);
66 }
67}
68
69/**
70 * validate
71 *
72 * test validation function for values returning NaN
73 */
74void __cdecl validate_isnan(double x, double y, double z)
75{
76 double result = fma(x, y, z);
77
78 if (!_isnan(result))
79 {
80 Fail("fma(%g, %g, %g) returned %20.17g when it should have returned %20.17g",
81 x, y, z, result, PAL_NAN);
82 }
83}
84
85/**
86 * main
87 *
88 * executable entry point
89 */
90int __cdecl main(int argc, char **argv)
91{
92 struct test tests[] =
93 {
94 /* x y z expected variance */
95 { PAL_NEGINF, PAL_NEGINF, PAL_NEGINF, PAL_NEGINF, 0 },
96 { -1e308, 2, 1e308, -1e308, 0 },
97 { 1e308, 2, -1e308, 1e308, 0 },
98 { PAL_POSINF, PAL_POSINF, PAL_POSINF, PAL_POSINF, 0 },
99 };
100
101 if (PAL_Initialize(argc, argv) != 0)
102 {
103 return FAIL;
104 }
105
106 for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
107 {
108 validate(tests[i].x, tests[i].y, tests[i].z, tests[i].expected, tests[i].variance);
109 }
110
111 // Returns NaN if x or y is infinite, the other is zero, and z is NaN
112 validate_isnan(PAL_NEGINF, 0, PAL_NAN);
113 validate_isnan(PAL_POSINF, 0, PAL_NAN);
114 validate_isnan(0, PAL_NEGINF, PAL_NAN);
115 validate_isnan(0, PAL_POSINF, PAL_NAN);
116
117 // Returns NaN if x or y is infinite, the other is zero, and z is not-NaN
118 validate_isnan(PAL_POSINF, 0, PAL_NEGINF);
119 validate_isnan(PAL_NEGINF, 0, PAL_NEGINF);
120 validate_isnan(0, PAL_POSINF, PAL_NEGINF);
121 validate_isnan(0, PAL_NEGINF, PAL_NEGINF);
122
123 validate_isnan(PAL_POSINF, 0, 0);
124 validate_isnan(PAL_NEGINF, 0, 0);
125 validate_isnan(0, PAL_POSINF, 0);
126 validate_isnan(0, PAL_NEGINF, 0);
127
128 validate_isnan(PAL_POSINF, 0, PAL_POSINF);
129 validate_isnan(PAL_NEGINF, 0, PAL_POSINF);
130 validate_isnan(0, PAL_POSINF, PAL_POSINF);
131 validate_isnan(0, PAL_NEGINF, PAL_POSINF);
132
133 // Returns NaN if (x * y) is infinite, and z is an infinite of the opposite sign
134 validate_isnan(PAL_POSINF, PAL_POSINF, PAL_NEGINF);
135 validate_isnan(PAL_NEGINF, PAL_NEGINF, PAL_NEGINF);
136 validate_isnan(PAL_POSINF, PAL_NEGINF, PAL_POSINF);
137 validate_isnan(PAL_NEGINF, PAL_POSINF, PAL_POSINF);
138
139 validate_isnan(PAL_POSINF, 1, PAL_NEGINF);
140 validate_isnan(PAL_NEGINF, 1, PAL_POSINF);
141 validate_isnan(1, PAL_POSINF, PAL_NEGINF);
142 validate_isnan(1, PAL_NEGINF, PAL_POSINF);
143
144 PAL_Terminate();
145 return PASS;
146}
147