blob: 4228f99ff5c1bb67b877482ea7c26fd34f6cab03 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
Alexander Steffen55f33242017-06-30 09:22:17 +02002 * This file is part of the MicroPython project, http://micropython.org/
Damien George04b91472014-05-03 23:27:38 +01003 *
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
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020027#include "py/mpconfig.h"
28#if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE
29
Damien Georgecea6cf82016-03-15 12:21:56 +000030#include <assert.h>
stijn861670b2015-05-16 10:54:19 +020031#include <stdlib.h>
32#include <stdint.h>
Damien George08a19662017-10-04 23:15:55 +110033#include <math.h>
stijn861670b2015-05-16 10:54:19 +020034#include "py/formatfloat.h"
35
Damien George8bfec2b2014-03-10 13:27:02 +000036/***********************************************************************
37
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020038 Routine for converting a arbitrary floating
stijn861670b2015-05-16 10:54:19 +020039 point number into a string.
Damien George8bfec2b2014-03-10 13:27:02 +000040
Dave Hylandsca5a2412014-03-10 00:10:01 -070041 The code in this funcion was inspired from Fred Bayer's pdouble.c.
42 Since pdouble.c was released as Public Domain, I'm releasing this
43 code as public domain as well.
Damien George8bfec2b2014-03-10 13:27:02 +000044
Dave Hylandsca5a2412014-03-10 00:10:01 -070045 The original code can be found in https://github.com/dhylands/format-float
Damien George8bfec2b2014-03-10 13:27:02 +000046
Dave Hylandsca5a2412014-03-10 00:10:01 -070047 Dave Hylands
Damien George8bfec2b2014-03-10 13:27:02 +000048
Dave Hylandsca5a2412014-03-10 00:10:01 -070049***********************************************************************/
50
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020051#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
Dave Hylandsca5a2412014-03-10 00:10:01 -070052// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
53// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
54// exponent is stored with a bias of 127.
55// The min and max floats are on the order of 1x10^37 and 1x10^-37
56
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020057#define FPTYPE float
58#define FPCONST(x) x##F
59#define FPROUND_TO_ONE 0.9999995F
60#define FPDECEXP 32
Damien George7417ccf2016-01-29 11:39:12 +000061#define FPMIN_BUF_SIZE 6 // +9e+99
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020062
Dave Hylandsca5a2412014-03-10 00:10:01 -070063#define FLT_SIGN_MASK 0x80000000
64#define FLT_EXP_MASK 0x7F800000
65#define FLT_MAN_MASK 0x007FFFFF
66
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020067union floatbits {
68 float f;
69 uint32_t u;
70};
71static inline int fp_signbit(float x) { union floatbits fb = {x}; return fb.u & FLT_SIGN_MASK; }
Damien Georgedc948e42017-10-10 16:27:54 +110072#define fp_isnan(x) isnan(x)
73#define fp_isinf(x) isinf(x)
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020074static inline int fp_iszero(float x) { union floatbits fb = {x}; return fb.u == 0; }
75static inline int fp_isless1(float x) { union floatbits fb = {x}; return fb.u < 0x3f800000; }
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020076
77#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
78
79#define FPTYPE double
80#define FPCONST(x) x
81#define FPROUND_TO_ONE 0.999999999995
82#define FPDECEXP 256
Damien George7417ccf2016-01-29 11:39:12 +000083#define FPMIN_BUF_SIZE 7 // +9e+199
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020084#define fp_signbit(x) signbit(x)
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020085#define fp_isnan(x) isnan(x)
86#define fp_isinf(x) isinf(x)
87#define fp_iszero(x) (x == 0)
88#define fp_isless1(x) (x < 1.0)
89
90#endif
91
92static const FPTYPE g_pos_pow[] = {
93 #if FPDECEXP > 32
94 1e256, 1e128, 1e64,
95 #endif
Dave Hylandsca5a2412014-03-10 00:10:01 -070096 1e32, 1e16, 1e8, 1e4, 1e2, 1e1
97};
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +020098static const FPTYPE g_neg_pow[] = {
99 #if FPDECEXP > 32
100 1e-256, 1e-128, 1e-64,
101 #endif
Dave Hylandsca5a2412014-03-10 00:10:01 -0700102 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
103};
104
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200105int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700106
107 char *s = buf;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700108
Damien George7417ccf2016-01-29 11:39:12 +0000109 if (buf_size <= FPMIN_BUF_SIZE) {
110 // FPMIN_BUF_SIZE is the minimum size needed to store any FP number.
111 // If the buffer does not have enough room for this (plus null terminator)
112 // then don't try to format the float.
Dave Hylandsca5a2412014-03-10 00:10:01 -0700113
114 if (buf_size >= 2) {
115 *s++ = '?';
116 }
117 if (buf_size >= 1) {
Damien Georgef5309fc2017-08-21 22:04:23 +1000118 *s = '\0';
Dave Hylandsca5a2412014-03-10 00:10:01 -0700119 }
120 return buf_size >= 2;
121 }
Damien Georgedc948e42017-10-10 16:27:54 +1100122 if (fp_signbit(f) && !fp_isnan(f)) {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700123 *s++ = '-';
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200124 f = -f;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700125 } else {
126 if (sign) {
127 *s++ = sign;
128 }
129 }
Damien George7417ccf2016-01-29 11:39:12 +0000130
131 // buf_remaining contains bytes available for digits and exponent.
132 // It is buf_size minus room for the sign and null byte.
133 int buf_remaining = buf_size - 1 - (s - buf);
Dave Hylandsca5a2412014-03-10 00:10:01 -0700134
Damien Georgedc948e42017-10-10 16:27:54 +1100135 {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700136 char uc = fmt & 0x20;
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200137 if (fp_isinf(f)) {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700138 *s++ = 'I' ^ uc;
139 *s++ = 'N' ^ uc;
140 *s++ = 'F' ^ uc;
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200141 goto ret;
142 } else if (fp_isnan(f)) {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700143 *s++ = 'N' ^ uc;
144 *s++ = 'A' ^ uc;
145 *s++ = 'N' ^ uc;
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200146 ret:
147 *s = '\0';
148 return s - buf;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700149 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700150 }
151
152 if (prec < 0) {
153 prec = 6;
154 }
155 char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
156 fmt |= 0x20; // Force fmt to be lowercase
157 char org_fmt = fmt;
158 if (fmt == 'g' && prec == 0) {
159 prec = 1;
160 }
Damien George761e4c72017-07-19 13:12:10 +1000161 int e, e1;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700162 int dec = 0;
163 char e_sign = '\0';
164 int num_digits = 0;
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200165 const FPTYPE *pos_pow = g_pos_pow;
166 const FPTYPE *neg_pow = g_neg_pow;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700167
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200168 if (fp_iszero(f)) {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700169 e = 0;
Damien Georgee1e76572016-03-29 22:07:15 +0100170 if (fmt == 'f') {
171 // Truncate precision to prevent buffer overflow
172 if (prec + 2 > buf_remaining) {
173 prec = buf_remaining - 2;
174 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700175 num_digits = prec + 1;
Damien Georgee1e76572016-03-29 22:07:15 +0100176 } else {
177 // Truncate precision to prevent buffer overflow
178 if (prec + 6 > buf_remaining) {
179 prec = buf_remaining - 6;
180 }
181 if (fmt == 'e') {
182 e_sign = '+';
183 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700184 }
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200185 } else if (fp_isless1(f)) {
Paul Sokolovsky3d6240b2015-11-22 20:02:16 +0200186 // We need to figure out what an integer digit will be used
187 // in case 'f' is used (or we revert other format to it below).
188 // As we just tested number to be <1, this is obviously 0,
189 // but we can round it up to 1 below.
190 char first_dig = '0';
191 if (f >= FPROUND_TO_ONE) {
192 first_dig = '1';
193 }
194
Dave Hylandsca5a2412014-03-10 00:10:01 -0700195 // Build negative exponent
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200196 for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
197 if (*neg_pow > f) {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700198 e += e1;
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200199 f *= *pos_pow;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700200 }
201 }
Dave Hylandsb2178692015-04-12 00:36:00 -0700202 char e_sign_char = '-';
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200203 if (fp_isless1(f) && f >= FPROUND_TO_ONE) {
204 f = FPCONST(1.0);
Dave Hylandsb2178692015-04-12 00:36:00 -0700205 if (e == 0) {
206 e_sign_char = '+';
207 }
Damien George03b8bb72016-03-29 22:03:13 +0100208 } else if (fp_isless1(f)) {
Damien George761e4c72017-07-19 13:12:10 +1000209 e++;
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200210 f *= FPCONST(10.0);
Dave Hylandsca5a2412014-03-10 00:10:01 -0700211 }
212
213 // If the user specified 'g' format, and e is <= 4, then we'll switch
214 // to the fixed format ('f')
215
216 if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
217 fmt = 'f';
218 dec = -1;
Dave Hylandsb2178692015-04-12 00:36:00 -0700219 *s++ = first_dig;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700220
Dave Hylandsca5a2412014-03-10 00:10:01 -0700221 if (org_fmt == 'g') {
222 prec += (e - 1);
223 }
Damien Georgecea6cf82016-03-15 12:21:56 +0000224
225 // truncate precision to prevent buffer overflow
226 if (prec + 2 > buf_remaining) {
227 prec = buf_remaining - 2;
228 }
229
Dave Hylandsca5a2412014-03-10 00:10:01 -0700230 num_digits = prec;
231 if (num_digits) {
Damien George761e4c72017-07-19 13:12:10 +1000232 *s++ = '.';
Dave Hylandsca5a2412014-03-10 00:10:01 -0700233 while (--e && num_digits) {
234 *s++ = '0';
235 num_digits--;
236 }
237 }
238 } else {
239 // For e & g formats, we'll be printing the exponent, so set the
240 // sign.
Dave Hylandsb2178692015-04-12 00:36:00 -0700241 e_sign = e_sign_char;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700242 dec = 0;
243
Damien George7417ccf2016-01-29 11:39:12 +0000244 if (prec > (buf_remaining - FPMIN_BUF_SIZE)) {
245 prec = buf_remaining - FPMIN_BUF_SIZE;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700246 if (fmt == 'g') {
247 prec++;
248 }
249 }
250 }
251 } else {
252 // Build positive exponent
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200253 for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
254 if (*pos_pow <= f) {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700255 e += e1;
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200256 f *= *neg_pow;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700257 }
258 }
259
Damien George03b8bb72016-03-29 22:03:13 +0100260 // It can be that f was right on the edge of an entry in pos_pow needs to be reduced
261 if (f >= FPCONST(10.0)) {
262 e += 1;
263 f *= FPCONST(0.1);
264 }
265
Damien George761e4c72017-07-19 13:12:10 +1000266 // If the user specified fixed format (fmt == 'f') and e makes the
Dave Hylandsca5a2412014-03-10 00:10:01 -0700267 // number too big to fit into the available buffer, then we'll
268 // switch to the 'e' format.
269
270 if (fmt == 'f') {
271 if (e >= buf_remaining) {
272 fmt = 'e';
273 } else if ((e + prec + 2) > buf_remaining) {
274 prec = buf_remaining - e - 2;
275 if (prec < 0) {
276 // This means no decimal point, so we can add one back
277 // for the decimal.
278 prec++;
279 }
280 }
281 }
Damien George7417ccf2016-01-29 11:39:12 +0000282 if (fmt == 'e' && prec > (buf_remaining - FPMIN_BUF_SIZE)) {
283 prec = buf_remaining - FPMIN_BUF_SIZE;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700284 }
Damien Georgee1e76572016-03-29 22:07:15 +0100285 if (fmt == 'g'){
286 // Truncate precision to prevent buffer overflow
287 if (prec + (FPMIN_BUF_SIZE - 1) > buf_remaining) {
288 prec = buf_remaining - (FPMIN_BUF_SIZE - 1);
289 }
290 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700291 // If the user specified 'g' format, and e is < prec, then we'll switch
292 // to the fixed format.
293
294 if (fmt == 'g' && e < prec) {
295 fmt = 'f';
296 prec -= (e + 1);
297 }
298 if (fmt == 'f') {
299 dec = e;
300 num_digits = prec + e + 1;
301 } else {
302 e_sign = '+';
303 }
304 }
305 if (prec < 0) {
306 // This can happen when the prec is trimmed to prevent buffer overflow
307 prec = 0;
308 }
309
310 // We now have num.f as a floating point number between >= 1 and < 10
311 // (or equal to zero), and e contains the absolute value of the power of
312 // 10 exponent. and (dec + 1) == the number of dgits before the decimal.
313
314 // For e, prec is # digits after the decimal
315 // For f, prec is # digits after the decimal
316 // For g, prec is the max number of significant digits
317 //
318 // For e & g there will be a single digit before the decimal
319 // for f there will be e digits before the decimal
320
321 if (fmt == 'e') {
322 num_digits = prec + 1;
323 } else if (fmt == 'g') {
324 if (prec == 0) {
325 prec = 1;
326 }
Damien George761e4c72017-07-19 13:12:10 +1000327 num_digits = prec;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700328 }
329
330 // Print the digits of the mantissa
331 for (int i = 0; i < num_digits; ++i, --dec) {
Tom Collins145796f2017-06-30 16:23:29 -0700332 int32_t d = (int32_t)f;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700333 *s++ = '0' + d;
334 if (dec == 0 && prec > 0) {
335 *s++ = '.';
336 }
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200337 f -= (FPTYPE)d;
338 f *= FPCONST(10.0);
Dave Hylandsca5a2412014-03-10 00:10:01 -0700339 }
340
341 // Round
Paul Sokolovsky3c4c0692015-11-22 18:09:28 +0200342 // If we print non-exponential format (i.e. 'f'), but a digit we're going
343 // to round by (e) is too far away, then there's nothing to round.
344 if ((org_fmt != 'f' || e <= 1) && f >= FPCONST(5.0)) {
Dave Hylandsca5a2412014-03-10 00:10:01 -0700345 char *rs = s;
346 rs--;
347 while (1) {
348 if (*rs == '.') {
349 rs--;
350 continue;
351 }
352 if (*rs < '0' || *rs > '9') {
353 // + or -
354 rs++; // So we sit on the digit to the right of the sign
355 break;
356 }
357 if (*rs < '9') {
358 (*rs)++;
359 break;
360 }
361 *rs = '0';
362 if (rs == buf) {
363 break;
364 }
Damien George761e4c72017-07-19 13:12:10 +1000365 rs--;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700366 }
367 if (*rs == '0') {
368 // We need to insert a 1
369 if (rs[1] == '.' && fmt != 'f') {
370 // We're going to round 9.99 to 10.00
371 // Move the decimal point
372 rs[0] = '.';
373 rs[1] = '0';
374 if (e_sign == '-') {
375 e--;
Damien George6ed45812017-06-13 13:36:56 +1000376 if (e == 0) {
377 e_sign = '+';
378 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700379 } else {
Damien George761e4c72017-07-19 13:12:10 +1000380 e++;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700381 }
Damien George6ed45812017-06-13 13:36:56 +1000382 } else {
383 // Need at extra digit at the end to make room for the leading '1'
384 s++;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700385 }
Damien George761e4c72017-07-19 13:12:10 +1000386 char *ss = s;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700387 while (ss > rs) {
388 *ss = ss[-1];
389 ss--;
390 }
391 *rs = '1';
392 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700393 }
394
Damien Georgee1e76572016-03-29 22:07:15 +0100395 // verify that we did not overrun the input buffer so far
396 assert((size_t)(s + 1 - buf) <= buf_size);
397
Dave Hylandsca5a2412014-03-10 00:10:01 -0700398 if (org_fmt == 'g' && prec > 0) {
399 // Remove trailing zeros and a trailing decimal point
400 while (s[-1] == '0') {
401 s--;
402 }
403 if (s[-1] == '.') {
404 s--;
405 }
406 }
407 // Append the exponent
408 if (e_sign) {
409 *s++ = e_char;
410 *s++ = e_sign;
Damien George7417ccf2016-01-29 11:39:12 +0000411 if (FPMIN_BUF_SIZE == 7 && e >= 100) {
412 *s++ = '0' + (e / 100);
413 }
414 *s++ = '0' + ((e / 10) % 10);
Dave Hylandsca5a2412014-03-10 00:10:01 -0700415 *s++ = '0' + (e % 10);
416 }
417 *s = '\0';
418
Damien Georgecea6cf82016-03-15 12:21:56 +0000419 // verify that we did not overrun the input buffer
420 assert((size_t)(s + 1 - buf) <= buf_size);
421
Dave Hylandsca5a2412014-03-10 00:10:01 -0700422 return s - buf;
423}
424
Paul Sokolovsky9aaccd42015-11-22 15:42:09 +0200425#endif // MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE