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 #if __SIZEOF_LONG_DOUBLE__ > __SIZEOF_DOUBLE__
37 
38 #define _NEED_IO_LONG_DOUBLE
39 
40 #include "dtoa.h"
41 
42 int
fcvtl_r(long double invalue,int ndecimal,int * decpt,int * sign,char * buf,size_t len)43 fcvtl_r (long double invalue,
44         int ndecimal,
45         int *decpt,
46         int *sign,
47         char *buf,
48         size_t len)
49 {
50     struct dtoa dtoa;
51     int ntrailing;      /* number of zeros to add after the value */
52     int ndigit;         /* numer of generated digits */
53     int dtoa_decimal = ndecimal;
54     char *digits = dtoa.digits;
55 
56     if (!isfinite(invalue)) {
57         ndigit = 3;
58         ntrailing = 0;
59         *sign = invalue < 0;
60         *decpt = 0;
61         if (isnan(invalue))
62             digits = "nan";
63         else
64             digits = "inf";
65     } else {
66         /* ndecimal = digits after decimal point desired
67          * ndigit = digits actually generated
68          * dtoa.exp = exponent (position of decimal relative to first digit generated)
69          */
70         if (ndecimal < 0)
71             dtoa_decimal = 0;
72 #ifdef _NEED_IO_FLOAT_LARGE
73         ndigit = __ldtoa_engine(invalue, &dtoa, LDTOA_MAX_DIG, true, ndecimal);
74 #elif __SIZEOF_LONG_DOUBLE__ == 8
75         ndigit = __dtoa_engine((FLOAT64) invalue, &dtoa, DTOA_MAX_DIG, true, ndecimal);
76 #elif __SIZEOF_LONG_DOUBLE__ == 4
77         ndigit = __ftoa_engine ((float) invalue, &dtoa, FTOA_MAX_DIG, true, ndecimal);
78 #endif
79         *sign = !!(dtoa.flags & DTOA_MINUS);
80 
81         /*
82          * To compute the number of zeros added after the value, there are
83          * three cases:
84          *
85          * 1. all of the generated digits are left of the decimal
86          *    point (dtoa.exp >= ndigit). We need (dtoa.exp - ndigit)
87          *    digits left of the decimal and ndecimal right of the
88          *    decimal: (dtoa.exp - ndigit) + ndecimal
89          *
90          * 2. some of the generated digits are right
91          *    of the decimal point (dtoa.exp < ndigit). We need
92          *    ndecimal digits total, but we have (ndigit - dtoa.exp)
93          *    already, so: ndecimal - (ndigit - dtoa.exp).
94          *
95          * 3. all of the generated digits are right of the decimal point
96          *    We need fewer than ndecimal digits by the magnitude of
97          *    the exponent (which is negative in this case, so:
98          *    ndecimal - (-dtoa.exp - 1) - ndigit
99          *
100          * These all turn out to be the same computation. Kinda cool.
101          */
102         ntrailing = (dtoa.exp + 1 - ndigit) + dtoa_decimal;
103         /*
104          * If this value is negative, then we actually have *no* digits to
105          * generate. In this case, we return the empty string and set the
106          * exponent to the number of digits requested (as they're all
107          * zero)
108          */
109         if (ntrailing < 0) {
110             ntrailing = 0;
111             ndigit = 0;
112 
113             /*
114              * Adjust exponent to reflect the desired output of ndecimal
115              * zeros
116              */
117             dtoa.exp = -(dtoa_decimal + 1);
118         }
119         *decpt = dtoa.exp + 1;
120     }
121 
122     /* If we can't fit the whole value in the provided space,
123      * return an error
124      */
125     if ((size_t) (ndigit + ntrailing) >= len)
126         return -1;
127 
128     /* Value */
129     memcpy(buf, digits, ndigit);
130     buf += ndigit;
131 
132     /* Trailing zeros */
133     memset(buf, '0', ntrailing);
134     buf += ntrailing;
135 
136     /* Null terminate */
137     buf[0] = '\0';
138     return 0;
139 }
140 
141 #elif __SIZEOF_LONG_DOUBLE__ == 4
142 
143 #include "stdio_private.h"
144 
145 int
fcvtl_r(long double invalue,int ndecimal,int * decpt,int * sign,char * buf,size_t len)146 fcvtl_r (long double invalue,
147          int ndecimal,
148          int *decpt,
149          int *sign,
150          char *buf,
151          size_t len)
152 {
153     return fcvtf_r((float) invalue, ndecimal, decpt, sign, buf, len);
154 }
155 
156 #elif __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__
157 
158 #include "stdio_private.h"
159 
160 int
fcvtl_r(long double invalue,int ndecimal,int * decpt,int * sign,char * buf,size_t len)161 fcvtl_r (long double invalue,
162          int ndecimal,
163          int *decpt,
164          int *sign,
165          char *buf,
166          size_t len)
167 {
168     return fcvt_r((double) invalue, ndecimal, decpt, sign, buf, len);
169 }
170 
171 #endif
172