1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright © 2022 Keith Packard
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above
14  *    copyright notice, this list of conditions and the following
15  *    disclaimer in the documentation and/or other materials provided
16  *    with the distribution.
17  *
18  * 3. Neither the name of the copyright holder nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33  * OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #define _GNU_SOURCE
37 #include <math.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <float.h>
41 
42 #ifdef LDBL_MANT_DIG
43 
44 static long double max_error;
45 
46 bool
within_error(long double expect,long double result,long double error)47 within_error(long double expect, long double result, long double error)
48 {
49     long double difference;
50     long double e = 1.0L;
51 
52     if (isnan(expect) && isnan(result))
53         return true;
54 
55     if (expect == result)
56         return true;
57 
58     if (expect != 0)
59         e = scalbnl(1.0L, -ilogbl(expect));
60 
61     difference = fabsl(expect - result) * e;
62 
63     if (difference > max_error)
64         max_error = difference;
65 
66     return difference <= error;
67 }
68 
69 int
check_long_double(const char * name,int i,long double prec,long double expect,long double result)70 check_long_double(const char *name, int i, long double prec, long double expect, long double result)
71 {
72     if (!within_error(expect, result, prec)) {
73         long double diff = fabsl(expect - result);
74 #ifdef __PICOLIBC__
75         printf("%s test %d got %.15g expect %.15g diff %.15g\n", name, i, (double) result, (double) expect, (double) diff);
76 #else
77 //        printf("%s test %d got %.33Lg expect %.33Lg diff %.33Lg\n", name, i, result, expect, diff);
78         printf("%s test %d got %La expect %La diff %La\n", name, i, result, expect, diff);
79 #endif
80         return 1;
81     }
82     return 0;
83 }
84 
85 int
check_long_long(const char * name,int i,long long expect,long long result)86 check_long_long(const char *name, int i, long long expect, long long result)
87 {
88     if (expect != result) {
89         long long diff = expect - result;
90         printf("%s test %d got %lld expect %lld diff %lld\n", name, i, result, expect, diff);
91         return 1;
92     }
93     return 0;
94 }
95 
96 typedef struct {
97     const char *name;
98     int (*test)(void);
99 } long_double_test_t;
100 
101 typedef struct {
102     int line;
103     long double x;
104     long double y;
105 } long_double_test_f_f_t;
106 
107 typedef struct {
108     int line;
109     long double x0;
110     long double x1;
111     long double y;
112 } long_double_test_f_ff_t;
113 
114 typedef struct {
115     int line;
116     long double x0;
117     int x1;
118     long double y;
119 } long_double_test_f_fi_t;
120 
121 typedef struct {
122     int line;
123     long double x;
124     long long y;
125 } long_double_test_i_f_t;
126 
127 /*
128  * sqrtl is "exact", but can have up to one bit of error as it might
129  * not have enough guard bits to correctly perform rounding, leading
130  * to some answers getting rounded to an even value instead of the
131  * (more accurate) odd value
132  */
133 #if LDBL_MANT_DIG == 64
134 #define DEFAULT_PREC 0x1p-55L
135 #define SQRTL_PREC 0x1.0p-63L
136 #define FULL_LONG_DOUBLE
137 #elif LDBL_MANT_DIG == 113
138 #define FULL_LONG_DOUBLE
139 #define DEFAULT_PREC 0x1p-105L
140 #define SQRTL_PREC 0x1.0p-112L
141 #elif LDBL_MANT_DIG == 106
142 #define DEFAULT_PREC 0x1p-97L
143 #define SQRTL_PREC 0x1.0p-105L
144 #define PART_LONG_DOUBLE
145 #elif LDBL_MANT_DIG == 53
146 #define DEFAULT_PREC 0x1p-48L
147 #define SQRTL_PREC 0x1.0p-52L
148 #else
149 #error unsupported long double
150 #endif
151 
152 #define HYPOTL_PREC     SQRTL_PREC
153 #define CBRTL_PREC      SQRTL_PREC
154 #define CEILL_PREC      0
155 #define FLOORL_PREC     0
156 #define LOGBL_PREC      0
157 #define RINTL_PREC      0
158 #define FMINL_PREC      0
159 #define FMAXL_PREC      0
160 #define SCALBNL_PREC    0
161 #define SCALBL_PREC     0
162 #define LDEXPL_PREC     0
163 #define COPYSIGNL_PREC  0
164 #define NEARBYINTL_PREC 0
165 #define ROUNDL_PREC     0
166 #define TRUNCL_PREC     0
167 
168 #include "long_double_vec.h"
169 
170 #if defined(_WANT_IO_LONG_DOUBLE) && (defined(TINY_STDIO) || defined(FLOATING_POINT))
171 #define TEST_IO_LONG_DOUBLE
172 #endif
173 
174 #ifdef TEST_IO_LONG_DOUBLE
175 static long double vals[] = {
176     1.0L,
177     0x1.8p0L,
178     3.141592653589793238462643383279502884197169L,
179     0.0L,
180 };
181 
182 #define NVALS   (sizeof(vals)/sizeof(vals[0]))
183 
184 static int
test_io(void)185 test_io(void)
186 {
187     int e;
188     int result = 0;
189     char buf[80];
190     unsigned i;
191 
192     for (e = __LDBL_MIN_EXP__ - __LDBL_MANT_DIG__; e <= __LDBL_MAX_EXP__; e++)
193     {
194         long double v, r;
195         for (i = 0; i < NVALS; i++) {
196             v = ldexpl(vals[i], e);
197             sprintf(buf, "%La", v);
198             sscanf(buf, "%Lf", &r);
199             if (v != r) {
200                 printf("%d: \"%s\", is %La should be %La\n", e, buf, r, v);
201                 result++;
202             }
203         }
204     }
205     return result;
206 }
207 #endif
208 
main(void)209 int main(void)
210 {
211     int result = 0;
212     unsigned int i;
213 
214     printf("LDBL_MANT_DIG %d\n", LDBL_MANT_DIG);
215 #ifdef TEST_IO_LONG_DOUBLE
216     result += test_io();
217 #endif
218     for (i = 0; i < sizeof(long_double_tests) / sizeof(long_double_tests[0]); i++) {
219         result += long_double_tests[i].test();
220     }
221     return result != 0;
222 }
223 
224 #else
main(void)225 int main(void)
226 {
227     printf("no long double support\n");
228     return 0;
229 }
230 #endif
231