blob: e73215a7a83d6016824e77d8bbf8668f0c74beb6 [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
Damien George51dfcb42015-01-01 20:27:54 +000045#include "py/mpconfig.h"
Dave Hylandsca5a2412014-03-10 00:10:01 -070046
47#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
Damien George51dfcb42015-01-01 20:27:54 +000048
49#include "py/formatfloat.h"
Dave Hylandsca5a2412014-03-10 00:10:01 -070050
51// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
52// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
53// exponent is stored with a bias of 127.
54// The min and max floats are on the order of 1x10^37 and 1x10^-37
55
56#define FLT_SIGN_MASK 0x80000000
57#define FLT_EXP_MASK 0x7F800000
58#define FLT_MAN_MASK 0x007FFFFF
59
60static const float g_pos_pow[] = {
61 1e32, 1e16, 1e8, 1e4, 1e2, 1e1
62};
63static const float g_neg_pow[] = {
64 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
65};
66
Damien George51dfcb42015-01-01 20:27:54 +000067int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
Dave Hylandsca5a2412014-03-10 00:10:01 -070068
69 char *s = buf;
70 int buf_remaining = buf_size - 1;
71
72 union {
73 float f;
74 uint32_t u;
75 } num = {f};
76
77 if (buf_size < 7) {
78 // Smallest exp notion is -9e+99 which is 6 chars plus terminating
Paul Sokolovskye3737b82014-07-17 17:48:35 +030079 // null.
Dave Hylandsca5a2412014-03-10 00:10:01 -070080
81 if (buf_size >= 2) {
82 *s++ = '?';
83 }
84 if (buf_size >= 1) {
85 *s++ = '\0';
86 }
87 return buf_size >= 2;
88 }
89 if (num.u & FLT_SIGN_MASK) {
90 *s++ = '-';
91 num.u &= ~FLT_SIGN_MASK;
92 } else {
93 if (sign) {
94 *s++ = sign;
95 }
96 }
97 buf_remaining -= (s - buf); // Adjust for sign
98
99 if ((num.u & FLT_EXP_MASK) == FLT_EXP_MASK) {
100 char uc = fmt & 0x20;
101 if ((num.u & FLT_MAN_MASK) == 0) {
102 *s++ = 'I' ^ uc;
103 *s++ = 'N' ^ uc;
104 *s++ = 'F' ^ uc;
105 } else {
106 *s++ = 'N' ^ uc;
107 *s++ = 'A' ^ uc;
108 *s++ = 'N' ^ uc;
109 }
110 *s = '\0';
111 return s - buf;
112 }
113
114 if (prec < 0) {
115 prec = 6;
116 }
117 char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
118 fmt |= 0x20; // Force fmt to be lowercase
119 char org_fmt = fmt;
120 if (fmt == 'g' && prec == 0) {
121 prec = 1;
122 }
123 int e, e1;
124 int dec = 0;
125 char e_sign = '\0';
126 int num_digits = 0;
127 const float *pos_pow = g_pos_pow;
128 const float *neg_pow = g_neg_pow;
129
130 if (num.u == 0) {
131 e = 0;
132 if (fmt == 'e') {
133 e_sign = '+';
134 } else if (fmt == 'f') {
135 num_digits = prec + 1;
136 }
137 } else if (num.u < 0x3f800000) { // f < 1.0
138 // Build negative exponent
139 for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
140 if (*neg_pow > num.f) {
141 e += e1;
142 num.f *= *pos_pow;
143 }
144 }
145 if (num.f < 1.0F && num.f >= 0.9999995F) {
146 num.f = 1.0F;
147 } else {
148 e++;
149 num.f *= 10.0F;
150 }
151
152 // If the user specified 'g' format, and e is <= 4, then we'll switch
153 // to the fixed format ('f')
154
155 if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
156 fmt = 'f';
157 dec = -1;
158 *s++ = '0';
159
160 if (prec + e + 1 > buf_remaining) {
161 prec = buf_remaining - e - 1;
162 }
163
164 if (org_fmt == 'g') {
165 prec += (e - 1);
166 }
167 num_digits = prec;
168 if (num_digits) {
169 *s++ = '.';
170 while (--e && num_digits) {
171 *s++ = '0';
172 num_digits--;
173 }
174 }
175 } else {
176 // For e & g formats, we'll be printing the exponent, so set the
177 // sign.
178 e_sign = '-';
179 dec = 0;
180
181 if (prec > (buf_remaining - 6)) {
182 prec = buf_remaining - 6;
183 if (fmt == 'g') {
184 prec++;
185 }
186 }
187 }
188 } else {
189 // Build positive exponent
190 for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
191 if (*pos_pow <= num.f) {
192 e += e1;
193 num.f *= *neg_pow;
194 }
195 }
196
197 // If the user specified fixed format (fmt == 'f') and e makes the
198 // number too big to fit into the available buffer, then we'll
199 // switch to the 'e' format.
200
201 if (fmt == 'f') {
202 if (e >= buf_remaining) {
203 fmt = 'e';
204 } else if ((e + prec + 2) > buf_remaining) {
205 prec = buf_remaining - e - 2;
206 if (prec < 0) {
207 // This means no decimal point, so we can add one back
208 // for the decimal.
209 prec++;
210 }
211 }
212 }
213 if (fmt == 'e' && prec > (buf_remaining - 6)) {
214 prec = buf_remaining - 6;
215 }
216 // If the user specified 'g' format, and e is < prec, then we'll switch
217 // to the fixed format.
218
219 if (fmt == 'g' && e < prec) {
220 fmt = 'f';
221 prec -= (e + 1);
222 }
223 if (fmt == 'f') {
224 dec = e;
225 num_digits = prec + e + 1;
226 } else {
227 e_sign = '+';
228 }
229 }
230 if (prec < 0) {
231 // This can happen when the prec is trimmed to prevent buffer overflow
232 prec = 0;
233 }
234
235 // We now have num.f as a floating point number between >= 1 and < 10
236 // (or equal to zero), and e contains the absolute value of the power of
237 // 10 exponent. and (dec + 1) == the number of dgits before the decimal.
238
239 // For e, prec is # digits after the decimal
240 // For f, prec is # digits after the decimal
241 // For g, prec is the max number of significant digits
242 //
243 // For e & g there will be a single digit before the decimal
244 // for f there will be e digits before the decimal
245
246 if (fmt == 'e') {
247 num_digits = prec + 1;
248 } else if (fmt == 'g') {
249 if (prec == 0) {
250 prec = 1;
251 }
252 num_digits = prec;
253 }
254
255 // Print the digits of the mantissa
256 for (int i = 0; i < num_digits; ++i, --dec) {
257 int32_t d = num.f;
258 *s++ = '0' + d;
259 if (dec == 0 && prec > 0) {
260 *s++ = '.';
261 }
262 num.f -= (float)d;
263 num.f *= 10.0F;
264 }
265
266 // Round
267 if (num.f >= 5.0F) {
268 char *rs = s;
269 rs--;
270 while (1) {
271 if (*rs == '.') {
272 rs--;
273 continue;
274 }
275 if (*rs < '0' || *rs > '9') {
276 // + or -
277 rs++; // So we sit on the digit to the right of the sign
278 break;
279 }
280 if (*rs < '9') {
281 (*rs)++;
282 break;
283 }
284 *rs = '0';
285 if (rs == buf) {
286 break;
287 }
288 rs--;
289 }
290 if (*rs == '0') {
291 // We need to insert a 1
292 if (rs[1] == '.' && fmt != 'f') {
293 // We're going to round 9.99 to 10.00
294 // Move the decimal point
295 rs[0] = '.';
296 rs[1] = '0';
297 if (e_sign == '-') {
298 e--;
299 } else {
300 e++;
301 }
302 }
303 s++;
304 char *ss = s;
305 while (ss > rs) {
306 *ss = ss[-1];
307 ss--;
308 }
309 *rs = '1';
310 }
311 if (num.u < 0x3f800000 && fmt == 'f') {
312 // We rounded up to 1.0
313 prec--;
314 }
315 }
316
317 if (org_fmt == 'g' && prec > 0) {
318 // Remove trailing zeros and a trailing decimal point
319 while (s[-1] == '0') {
320 s--;
321 }
322 if (s[-1] == '.') {
323 s--;
324 }
325 }
326 // Append the exponent
327 if (e_sign) {
328 *s++ = e_char;
329 *s++ = e_sign;
330 *s++ = '0' + (e / 10);
331 *s++ = '0' + (e % 10);
332 }
333 *s = '\0';
334
335 return s - buf;
336}
337
338#endif