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 fmaf 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// binary32 (float) has a machine epsilon of 2^-23 (approx. 1.19e-07). However, this
18// is slightly too accurate when writing tests meant to run against libm implementations
19// for various platforms. 2^-21 (approx. 4.76e-07) 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 (6-9 digits).
24
25// For example, a test with an expect result in the format of 0.xxxxxxxxx will use PAL_EPSILON
26// for the variance, while an expected result in the format of 0.0xxxxxxxxx will use
27// PAL_EPSILON / 10 and and expected result in the format of x.xxxxxx will use PAL_EPSILON * 10.
28#define PAL_EPSILON 4.76837158e-07
29
30#define PAL_NAN sqrtf(-1.0f)
31#define PAL_POSINF -logf(0.0f)
32#define PAL_NEGINF logf(0.0f)
33
34/**
35 * Helper test structure
36 */
37struct test
38{
39 float x; /* first component of the value to test the function with */
40 float y; /* second component of the value to test the function with */
41 float z; /* third component of the value to test the function with */
42 float expected; /* expected result */
43 float variance; /* maximum delta between the expected and actual result */
44};
45
46/**
47 * validate
48 *
49 * test validation function
50 */
51void __cdecl validate(float x, float y, float z, float expected, float variance)
52{
53 float result = fmaf(x, y, z);
54
55 /*
56 * The test is valid when the difference between result
57 * and expected is less than or equal to variance
58 */
59 float delta = fabsf(result - expected);
60
61 if (delta > variance)
62 {
63 Fail("fmaf(%g, %g, %g) returned %10.9g when it should have returned %10.9g",
64 x, y, z, result, expected);
65 }
66}
67
68/**
69 * validate
70 *
71 * test validation function for values returning NaN
72 */
73void __cdecl validate_isnan(float x, float y, float z)
74{
75 float result = fmaf(x, y, z);
76
77 if (!_isnanf(result))
78 {
79 Fail("fmaf(%g, %g, %g) returned %10.9g when it should have returned %10.9g",
80 x, y, z, result, PAL_NAN);
81 }
82}
83
84/**
85 * main
86 *
87 * executable entry point
88 */
89int __cdecl main(int argc, char **argv)
90{
91 struct test tests[] =
92 {
93 /* x y z expected variance */
94 { PAL_NEGINF, PAL_NEGINF, PAL_NEGINF, PAL_NEGINF, 0 },
95 { -1e38, 2, 1e38, -1e38, 0 },
96 { 1e38, 2, -1e38, 1e38, 0 },
97 { PAL_POSINF, PAL_POSINF, PAL_POSINF, PAL_POSINF, 0 },
98 };
99
100 if (PAL_Initialize(argc, argv) != 0)
101 {
102 return FAIL;
103 }
104
105 for (int i = 0; i < (sizeof(tests) / sizeof(struct test)); i++)
106 {
107 validate(tests[i].x, tests[i].y, tests[i].z, tests[i].expected, tests[i].variance);
108 }
109
110 // Returns NaN if x or y is infinite, the other is zero, and z is NaN
111 validate_isnan(PAL_NEGINF, 0, PAL_NAN);
112 validate_isnan(PAL_POSINF, 0, PAL_NAN);
113 validate_isnan(0, PAL_NEGINF, PAL_NAN);
114 validate_isnan(0, PAL_POSINF, PAL_NAN);
115
116 // Returns NaN if x or y is infinite, the other is zero, and z is not-NaN
117 validate_isnan(PAL_POSINF, 0, PAL_NEGINF);
118 validate_isnan(PAL_NEGINF, 0, PAL_NEGINF);
119 validate_isnan(0, PAL_POSINF, PAL_NEGINF);
120 validate_isnan(0, PAL_NEGINF, PAL_NEGINF);
121
122 validate_isnan(PAL_POSINF, 0, 0);
123 validate_isnan(PAL_NEGINF, 0, 0);
124 validate_isnan(0, PAL_POSINF, 0);
125 validate_isnan(0, PAL_NEGINF, 0);
126
127 validate_isnan(PAL_POSINF, 0, PAL_POSINF);
128 validate_isnan(PAL_NEGINF, 0, PAL_POSINF);
129 validate_isnan(0, PAL_POSINF, PAL_POSINF);
130 validate_isnan(0, PAL_NEGINF, PAL_POSINF);
131
132 // Returns NaN if (x * y) is infinite, and z is an infinite of the opposite sign
133 validate_isnan(PAL_POSINF, PAL_POSINF, PAL_NEGINF);
134 validate_isnan(PAL_NEGINF, PAL_NEGINF, PAL_NEGINF);
135 validate_isnan(PAL_POSINF, PAL_NEGINF, PAL_POSINF);
136 validate_isnan(PAL_NEGINF, PAL_POSINF, PAL_POSINF);
137
138 validate_isnan(PAL_POSINF, 1, PAL_NEGINF);
139 validate_isnan(PAL_NEGINF, 1, PAL_POSINF);
140 validate_isnan(1, PAL_POSINF, PAL_NEGINF);
141 validate_isnan(1, PAL_NEGINF, PAL_POSINF);
142
143 PAL_Terminate();
144 return PASS;
145}
146