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 <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 
41 struct test {
42     double      value;
43     int         ndigit;
44     int         decpt;
45     int         sign;
46     char        *expect;
47 };
48 
49 struct test ecvt_tests[] = {
50     { 1.0e0,    4,   1, 0, "1000", },
51     { -1.0e0,   4,   1, 1, "1000", },
52     { 1.0e7,    7,   8, 0, "1000000" },
53     { 1.0e-12,  4, -11, 0, "1000" },
54 };
55 
56 #if !defined(TINY_STDIO) && !defined(NO_NEWLIB)
57 #define ecvt_r(n, dig, dec, sign, buf, len) (ecvtbuf(n, dig, dec, sign, buf) ? 0 : -1)
58 #define fcvt_r(n, dig, dec, sign, buf, len) (fcvtbuf(n, dig, dec, sign, buf) ? 0 : -1)
59 #define ecvtf_r(n, dig, dec, sign, buf, len) (ecvtbuf(n, dig, dec, sign, buf) ? 0 : -1)
60 #define fcvtf_r(n, dig, dec, sign, buf, len) (fcvtbuf(n, dig, dec, sign, buf) ? 0 : -1)
61 #define SKIP_FCVT_NEG
62 #endif
63 
64 #define N_ECVT_TESTS    (sizeof(ecvt_tests) / sizeof(ecvt_tests[0]))
65 
66 struct test fcvt_tests[] = {
67     { 0.0058882729652625027, 7, -2, 0, "58883", },
68     { 1.0e0,    4,   1, 0, "10000", },
69     { -1.0e0,   4,   1, 1, "10000", },
70     { 1.0e7,    7,   8, 0, "100000000000000" },
71     { 1.0e-12,  4,  -4, 0, "" },
72 #ifndef SKIP_FCVT_NEG
73     /* legacy newlib doesn't handle negative ndecimal correctly */
74     { 1.23456e0,-4,  1, 0, "1" },
75     { 1.23456e1,-4,  2, 0, "10" },
76     { 1.23456e2,-4,  3, 0, "100" },
77     { 1.23456e3,-4,  4, 0, "1000" },
78     { 1.23456e4,-4,  5, 0, "10000" },
79     { 1.23456e5,-4,  6, 0, "120000" },
80     { 1.23456e6,-4,  7, 0, "1230000" },
81 #endif
82     { 1.23456e-10,4,-4, 0, "" },
83     { 1.23456e-9,4, -4, 0, "" },
84     { 1.23456e-8,4, -4, 0, "" },
85     { 1.23456e-7,4, -4, 0, "" },
86     { 1.23456e-6,4, -4, 0, "" },
87     { 1.23456e-5,4, -4, 0, "" },
88     { 1.23456e-4,4, -3, 0, "1" },
89     { 1.23456e-3,4, -2, 0, "12" },
90     { 1.23456e-2,4, -1, 0, "123" },
91     { 1.23456e-1,4,  0, 0, "1235" },
92     { 1.23456e0,4,   1, 0, "12346" },
93     { 1.23456e1,4,   2, 0, "123456" },
94     { 1.23456e2,4,   3, 0, "1234560" },
95     { 1.23456e3,4,   4, 0, "12345600" },
96     { 1.23456e4,4,   5, 0, "123456000" },
97     { 1.23456e5,4,   6, 0, "1234560000" },
98     { 1.23456e6,4,   7, 0, "12345600000" },
99     { 1.23456e7,4,   8, 0, "123456000000" },
100     { 1.23456e8,4,   9, 0, "1234560000000" },
101     { 1.23456e9,4,  10, 0, "12345600000000" },
102 };
103 
104 #define N_FCVT_TESTS    (sizeof(fcvt_tests) / sizeof(fcvt_tests[0]))
105 
106 struct test fcvt_extra_tests[] = {
107 #ifdef TINY_STDIO
108     { 0x1.fffffffffffffp1023, 17, 309, 0, "17976931348623157"
109       "0000000000000000000000000000000000000000"
110       "0000000000000000000000000000000000000000"
111       "0000000000000000000000000000000000000000"
112       "0000000000000000000000000000000000000000"
113       "0000000000000000000000000000000000000000"
114       "0000000000000000000000000000000000000000"
115       "0000000000000000000000000000000000000000"
116       "000000000000"
117       "00000000000000000"},
118     { 0x1.fffffep127, 9, 39, 0, "340282346638528860000000000000000000000000000000" },
119 #else
120     { 0x1.fffffffffffffp1023, 17, 309, 0, "17976931348623157"
121       "0814527423731704356798070567525844996598"
122       "9174768031572607800285387605895586327668"
123       "7817154045895351438246423432132688946418"
124       "2768467546703537516986049910576551282076"
125       "2454900903893289440758685084551339423045"
126       "8323690322294816580855933212334827479782"
127       "6204144723168738177180919299881250404026"
128       "184124858368"
129       "00000000000000000"},
130     { 0x1.fffffep127, 9, 39, 0, "340282346638528859811704183484516925440000000000" },
131 #endif
132 };
133 
134 #define N_FCVT_EXTRA_TESTS      (sizeof(fcvt_extra_tests) / sizeof(fcvt_extra_tests[0]))
135 
136 struct test fcvtf_tests[] = {
137 #ifdef TINY_STDIO
138     { 0x1.fffffep127, 9, 39, 0, "340282350000000000000000000000000000000000000000" },
139 #else
140     /* legacy newlib uses the double path for this operation */
141     { 0x1.fffffep127, 9, 39, 0, "340282346638528859811704183484516925440000000000" },
142 #endif
143 };
144 
145 #define N_FCVTF_TESTS    (sizeof(fcvtf_tests) / sizeof(fcvtf_tests[0]))
146 
147 #if defined(TINY_STDIO) && !defined(_IO_FLOAT_EXACT)
148 /* non-exact tinystdio conversions are not precise over about 6 digits */
149 #define SKIP_LONG_FLOAT 1
150 #else
151 #define SKIP_LONG_FLOAT 0
152 #endif
153 
154 #define many_tests(tests, func, n, skip_long) do {                      \
155     for (i = 0; i < n; i++) {                                           \
156         int decpt;                                                      \
157         int sign;                                                       \
158         int ret = func(tests[i].value, tests[i].ndigit, &decpt, &sign, buf, sizeof(buf)); \
159                                                                         \
160         if (ret < 0) {                                                  \
161             printf("%d: failed\n", ret);                                \
162             continue;                                                   \
163         }                                                               \
164         if (skip_long && strlen(buf) > 6) {                             \
165             printf("skipping test as result is long (%s)\n", buf);      \
166             continue;                                                   \
167         }                                                               \
168         if (strcmp(buf, tests[i].expect) != 0 ||                        \
169             decpt != tests[i].decpt ||                                  \
170             sign != tests[i].sign)                                      \
171         {                                                               \
172             printf(#func ":%d got '%s' dec %d sign %d expect '%s' dec %d sign %d\n", \
173                    i, buf, decpt, sign,                                 \
174                    tests[i].expect, tests[i].decpt, tests[i].sign);     \
175             error = 1;                                                  \
176         }                                                               \
177     }                                                                   \
178     } while(0)
179 
180 int
main(void)181 main(void)
182 {
183     int error = 0;
184     unsigned i;
185     static char buf[2048];
186 
187     many_tests(fcvt_tests, fcvt_r, N_FCVT_TESTS, 0);
188     many_tests(fcvt_extra_tests, fcvt_r, N_FCVT_EXTRA_TESTS, SKIP_LONG_FLOAT);
189     many_tests(ecvt_tests, ecvt_r, N_ECVT_TESTS, 0);
190 #ifdef __PICOLIBC__
191     many_tests(fcvt_tests, fcvtf_r, N_FCVT_TESTS, SKIP_LONG_FLOAT);
192     many_tests(fcvtf_tests, fcvtf_r, N_FCVTF_TESTS, SKIP_LONG_FLOAT);
193     many_tests(ecvt_tests, ecvtf_r, N_ECVT_TESTS, 0);
194 #endif
195     return error;
196 }
197