blob: d1b12cdd5bcf65185e843e015267a49956314a2e [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Damien George8bfec2b2014-03-10 13:27:02 +000027/***********************************************************************
28
29 formatfloat.c - Ruutine for converting a single-precision floating
Dave Hylandsca5a2412014-03-10 00:10:01 -070030 point number into a string.
Damien George8bfec2b2014-03-10 13:27:02 +000031
Dave Hylandsca5a2412014-03-10 00:10:01 -070032 The code in this funcion was inspired from Fred Bayer's pdouble.c.
33 Since pdouble.c was released as Public Domain, I'm releasing this
34 code as public domain as well.
Damien George8bfec2b2014-03-10 13:27:02 +000035
Dave Hylandsca5a2412014-03-10 00:10:01 -070036 The original code can be found in https://github.com/dhylands/format-float
Damien George8bfec2b2014-03-10 13:27:02 +000037
Dave Hylandsca5a2412014-03-10 00:10:01 -070038 Dave Hylands
Damien George8bfec2b2014-03-10 13:27:02 +000039
Dave Hylandsca5a2412014-03-10 00:10:01 -070040***********************************************************************/
41
Dave Hylandsca5a2412014-03-10 00:10:01 -070042#include <stdlib.h>
Dave Hylandsbaf6f142014-03-30 21:06:50 -070043#include <stdint.h>
Dave Hylandsca5a2412014-03-10 00:10:01 -070044
45#include "mpconfig.h"
46
47#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
Damien George8bfec2b2014-03-10 13:27:02 +000048#include "formatfloat.h"
Dave Hylandsca5a2412014-03-10 00:10:01 -070049
50// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
51// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
52// exponent is stored with a bias of 127.
53// The min and max floats are on the order of 1x10^37 and 1x10^-37
54
55#define FLT_SIGN_MASK 0x80000000
56#define FLT_EXP_MASK 0x7F800000
57#define FLT_MAN_MASK 0x007FFFFF
58
59static const float g_pos_pow[] = {
60 1e32, 1e16, 1e8, 1e4, 1e2, 1e1
61};
62static const float g_neg_pow[] = {
63 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
64};
65
66int format_float(float f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
67
68 char *s = buf;
69 int buf_remaining = buf_size - 1;
70
71 union {
72 float f;
73 uint32_t u;
74 } num = {f};
75
76 if (buf_size < 7) {
77 // Smallest exp notion is -9e+99 which is 6 chars plus terminating
Paul Sokolovskye3737b82014-07-17 17:48:35 +030078 // null.
Dave Hylandsca5a2412014-03-10 00:10:01 -070079
80 if (buf_size >= 2) {
81 *s++ = '?';
82 }
83 if (buf_size >= 1) {
84 *s++ = '\0';
85 }
86 return buf_size >= 2;
87 }
88 if (num.u & FLT_SIGN_MASK) {
89 *s++ = '-';
90 num.u &= ~FLT_SIGN_MASK;
91 } else {
92 if (sign) {
93 *s++ = sign;
94 }
95 }
96 buf_remaining -= (s - buf); // Adjust for sign
97
98 if ((num.u & FLT_EXP_MASK) == FLT_EXP_MASK) {
99 char uc = fmt & 0x20;
100 if ((num.u & FLT_MAN_MASK) == 0) {
101 *s++ = 'I' ^ uc;
102 *s++ = 'N' ^ uc;
103 *s++ = 'F' ^ uc;
104 } else {
105 *s++ = 'N' ^ uc;
106 *s++ = 'A' ^ uc;
107 *s++ = 'N' ^ uc;
108 }
109 *s = '\0';
110 return s - buf;
111 }
112
113 if (prec < 0) {
114 prec = 6;
115 }
116 char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
117 fmt |= 0x20; // Force fmt to be lowercase
118 char org_fmt = fmt;
119 if (fmt == 'g' && prec == 0) {
120 prec = 1;
121 }
122 int e, e1;
123 int dec = 0;
124 char e_sign = '\0';
125 int num_digits = 0;
126 const float *pos_pow = g_pos_pow;
127 const float *neg_pow = g_neg_pow;
128
129 if (num.u == 0) {
130 e = 0;
131 if (fmt == 'e') {
132 e_sign = '+';
133 } else if (fmt == 'f') {
134 num_digits = prec + 1;
135 }
136 } else if (num.u < 0x3f800000) { // f < 1.0
137 // Build negative exponent
138 for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
139 if (*neg_pow > num.f) {
140 e += e1;
141 num.f *= *pos_pow;
142 }
143 }
144 if (num.f < 1.0F && num.f >= 0.9999995F) {
145 num.f = 1.0F;
146 } else {
147 e++;
148 num.f *= 10.0F;
149 }
150
151 // If the user specified 'g' format, and e is <= 4, then we'll switch
152 // to the fixed format ('f')
153
154 if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
155 fmt = 'f';
156 dec = -1;
157 *s++ = '0';
158
159 if (prec + e + 1 > buf_remaining) {
160 prec = buf_remaining - e - 1;
161 }
162
163 if (org_fmt == 'g') {
164 prec += (e - 1);
165 }
166 num_digits = prec;
167 if (num_digits) {
168 *s++ = '.';
169 while (--e && num_digits) {
170 *s++ = '0';
171 num_digits--;
172 }
173 }
174 } else {
175 // For e & g formats, we'll be printing the exponent, so set the
176 // sign.
177 e_sign = '-';
178 dec = 0;
179
180 if (prec > (buf_remaining - 6)) {
181 prec = buf_remaining - 6;
182 if (fmt == 'g') {
183 prec++;
184 }
185 }
186 }
187 } else {
188 // Build positive exponent
189 for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
190 if (*pos_pow <= num.f) {
191 e += e1;
192 num.f *= *neg_pow;
193 }
194 }
195
196 // If the user specified fixed format (fmt == 'f') and e makes the
197 // number too big to fit into the available buffer, then we'll
198 // switch to the 'e' format.
199
200 if (fmt == 'f') {
201 if (e >= buf_remaining) {
202 fmt = 'e';
203 } else if ((e + prec + 2) > buf_remaining) {
204 prec = buf_remaining - e - 2;
205 if (prec < 0) {
206 // This means no decimal point, so we can add one back
207 // for the decimal.
208 prec++;
209 }
210 }
211 }
212 if (fmt == 'e' && prec > (buf_remaining - 6)) {
213 prec = buf_remaining - 6;
214 }
215 // If the user specified 'g' format, and e is < prec, then we'll switch
216 // to the fixed format.
217
218 if (fmt == 'g' && e < prec) {
219 fmt = 'f';
220 prec -= (e + 1);
221 }
222 if (fmt == 'f') {
223 dec = e;
224 num_digits = prec + e + 1;
225 } else {
226 e_sign = '+';
227 }
228 }
229 if (prec < 0) {
230 // This can happen when the prec is trimmed to prevent buffer overflow
231 prec = 0;
232 }
233
234 // We now have num.f as a floating point number between >= 1 and < 10
235 // (or equal to zero), and e contains the absolute value of the power of
236 // 10 exponent. and (dec + 1) == the number of dgits before the decimal.
237
238 // For e, prec is # digits after the decimal
239 // For f, prec is # digits after the decimal
240 // For g, prec is the max number of significant digits
241 //
242 // For e & g there will be a single digit before the decimal
243 // for f there will be e digits before the decimal
244
245 if (fmt == 'e') {
246 num_digits = prec + 1;
247 } else if (fmt == 'g') {
248 if (prec == 0) {
249 prec = 1;
250 }
251 num_digits = prec;
252 }
253
254 // Print the digits of the mantissa
255 for (int i = 0; i < num_digits; ++i, --dec) {
256 int32_t d = num.f;
257 *s++ = '0' + d;
258 if (dec == 0 && prec > 0) {
259 *s++ = '.';
260 }
261 num.f -= (float)d;
262 num.f *= 10.0F;
263 }
264
265 // Round
266 if (num.f >= 5.0F) {
267 char *rs = s;
268 rs--;
269 while (1) {
270 if (*rs == '.') {
271 rs--;
272 continue;
273 }
274 if (*rs < '0' || *rs > '9') {
275 // + or -
276 rs++; // So we sit on the digit to the right of the sign
277 break;
278 }
279 if (*rs < '9') {
280 (*rs)++;
281 break;
282 }
283 *rs = '0';
284 if (rs == buf) {
285 break;
286 }
287 rs--;
288 }
289 if (*rs == '0') {
290 // We need to insert a 1
291 if (rs[1] == '.' && fmt != 'f') {
292 // We're going to round 9.99 to 10.00
293 // Move the decimal point
294 rs[0] = '.';
295 rs[1] = '0';
296 if (e_sign == '-') {
297 e--;
298 } else {
299 e++;
300 }
301 }
302 s++;
303 char *ss = s;
304 while (ss > rs) {
305 *ss = ss[-1];
306 ss--;
307 }
308 *rs = '1';
309 }
310 if (num.u < 0x3f800000 && fmt == 'f') {
311 // We rounded up to 1.0
312 prec--;
313 }
314 }
315
316 if (org_fmt == 'g' && prec > 0) {
317 // Remove trailing zeros and a trailing decimal point
318 while (s[-1] == '0') {
319 s--;
320 }
321 if (s[-1] == '.') {
322 s--;
323 }
324 }
325 // Append the exponent
326 if (e_sign) {
327 *s++ = e_char;
328 *s++ = e_sign;
329 *s++ = '0' + (e / 10);
330 *s++ = '0' + (e % 10);
331 }
332 *s = '\0';
333
334 return s - buf;
335}
336
337#endif