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 "ftoa_engine.h"
38 #include <_ansi.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <math.h>
42 
43 int
fcvtf_r(float invalue,int ndecimal,int * decpt,int * sign,char * buf,size_t len)44 fcvtf_r (float invalue,
45          int ndecimal,
46          int *decpt,
47          int *sign,
48          char *buf,
49          size_t len)
50 {
51     struct ftoa ftoa;
52     int ntrailing;      /* number of zeros to add after the value */
53     int ndigit;         /* numer of generated digits */
54     int ftoa_decimal = ndecimal;
55     char *digits = ftoa.digits;
56 
57     if (!__finitef(invalue)) {
58         ndigit = 3;
59         ntrailing = 0;
60         *sign = invalue < 0;
61         *decpt = 0;
62         if (isnan(invalue))
63             digits = "nan";
64         else
65             digits = "inf";
66     } else {
67         /* ndecimal = digits after decimal point desired
68          * ndigit = digits actually generated
69          * ftoa.exp = exponent (position of decimal relative to first digit generated)
70          */
71         if (ndecimal < 0)
72             ftoa_decimal = 0;
73         ndigit = __ftoa_engine(invalue, &ftoa, FTOA_MAX_DIG, true, ndecimal);
74         *sign = !!(ftoa.flags & FTOA_MINUS);
75 
76         /*
77          * To compute the number of zeros added after the value, there are
78          * three cases:
79          *
80          * 1. all of the generated digits are left of the decimal
81          *    point (ftoa.exp >= ndigit). We need (ftoa.exp - ndigit)
82          *    digits left of the decimal and ndecimal right of the
83          *    decimal: (ftoa.exp - ndigit) + ndecimal
84          *
85          * 2. some of the generated digits are right
86          *    of the decimal point (ftoa.exp < ndigit). We need
87          *    ndecimal digits total, but we have (ndigit - ftoa.exp)
88          *    already, so: ndecimal - (ndigit - ftoa.exp).
89          *
90          * 3. all of the generated digits are right of the decimal point
91          *    We need fewer than ndecimal digits by the magnitude of
92          *    the exponent (which is negative in this case, so:
93          *    ndecimal - (-ftoa.exp - 1) - ndigit
94          *
95          * These all turn out to be the same computation. Kinda cool.
96          */
97         ntrailing = (ftoa.exp + 1 - ndigit) + ftoa_decimal;
98         /*
99          * If this value is negative, then we actually have *no* digits to
100          * generate. In this case, we return the empty string and set the
101          * exponent to the number of digits requested (as they're all
102          * zero)
103          */
104         if (ntrailing < 0) {
105             ntrailing = 0;
106             ndigit = 0;
107 
108             /*
109              * Adjust exponent to reflect the desired output of ndecimal
110              * zeros
111              */
112             ftoa.exp = -(ftoa_decimal + 1);
113         }
114         *decpt = ftoa.exp + 1;
115     }
116 
117     /* If we can't fit the whole value in the provided space,
118      * return an error
119      */
120     if ((size_t) (ndigit + ntrailing) >= len)
121         return -1;
122 
123     /* Value */
124     memcpy(buf, digits, ndigit);
125     len -= ndigit;
126     buf += ndigit;
127 
128     /* Trailing zeros */
129     memset(buf, '0', ntrailing);
130     buf += ntrailing;
131 
132     /* Null terminate */
133     buf[0] = '\0';
134     return 0;
135 }
136