blob: a801055a6538f46f3ec5cf105d4438701a09477f [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
stijn861670b2015-05-16 10:54:19 +020027#include <stdlib.h>
28#include <stdint.h>
29#include "py/formatfloat.h"
30
31#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
Damien George8bfec2b2014-03-10 13:27:02 +000032/***********************************************************************
33
stijn861670b2015-05-16 10:54:19 +020034 Routine for converting a single-precision floating
35 point number into a string.
Damien George8bfec2b2014-03-10 13:27:02 +000036
Dave Hylandsca5a2412014-03-10 00:10:01 -070037 The code in this funcion was inspired from Fred Bayer's pdouble.c.
38 Since pdouble.c was released as Public Domain, I'm releasing this
39 code as public domain as well.
Damien George8bfec2b2014-03-10 13:27:02 +000040
Dave Hylandsca5a2412014-03-10 00:10:01 -070041 The original code can be found in https://github.com/dhylands/format-float
Damien George8bfec2b2014-03-10 13:27:02 +000042
Dave Hylandsca5a2412014-03-10 00:10:01 -070043 Dave Hylands
Damien George8bfec2b2014-03-10 13:27:02 +000044
Dave Hylandsca5a2412014-03-10 00:10:01 -070045***********************************************************************/
46
Dave Hylandsca5a2412014-03-10 00:10:01 -070047// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
48// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
49// exponent is stored with a bias of 127.
50// The min and max floats are on the order of 1x10^37 and 1x10^-37
51
52#define FLT_SIGN_MASK 0x80000000
53#define FLT_EXP_MASK 0x7F800000
54#define FLT_MAN_MASK 0x007FFFFF
55
56static const float g_pos_pow[] = {
57 1e32, 1e16, 1e8, 1e4, 1e2, 1e1
58};
59static const float g_neg_pow[] = {
60 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
61};
62
Damien George51dfcb42015-01-01 20:27:54 +000063int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
Dave Hylandsca5a2412014-03-10 00:10:01 -070064
65 char *s = buf;
66 int buf_remaining = buf_size - 1;
67
68 union {
69 float f;
70 uint32_t u;
71 } num = {f};
72
73 if (buf_size < 7) {
74 // Smallest exp notion is -9e+99 which is 6 chars plus terminating
Paul Sokolovskye3737b82014-07-17 17:48:35 +030075 // null.
Dave Hylandsca5a2412014-03-10 00:10:01 -070076
77 if (buf_size >= 2) {
78 *s++ = '?';
79 }
80 if (buf_size >= 1) {
81 *s++ = '\0';
82 }
83 return buf_size >= 2;
84 }
85 if (num.u & FLT_SIGN_MASK) {
86 *s++ = '-';
87 num.u &= ~FLT_SIGN_MASK;
88 } else {
89 if (sign) {
90 *s++ = sign;
91 }
92 }
93 buf_remaining -= (s - buf); // Adjust for sign
94
95 if ((num.u & FLT_EXP_MASK) == FLT_EXP_MASK) {
96 char uc = fmt & 0x20;
97 if ((num.u & FLT_MAN_MASK) == 0) {
98 *s++ = 'I' ^ uc;
99 *s++ = 'N' ^ uc;
100 *s++ = 'F' ^ uc;
101 } else {
102 *s++ = 'N' ^ uc;
103 *s++ = 'A' ^ uc;
104 *s++ = 'N' ^ uc;
105 }
106 *s = '\0';
107 return s - buf;
108 }
109
110 if (prec < 0) {
111 prec = 6;
112 }
113 char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
114 fmt |= 0x20; // Force fmt to be lowercase
115 char org_fmt = fmt;
116 if (fmt == 'g' && prec == 0) {
117 prec = 1;
118 }
119 int e, e1;
120 int dec = 0;
121 char e_sign = '\0';
122 int num_digits = 0;
123 const float *pos_pow = g_pos_pow;
124 const float *neg_pow = g_neg_pow;
125
126 if (num.u == 0) {
127 e = 0;
128 if (fmt == 'e') {
129 e_sign = '+';
130 } else if (fmt == 'f') {
131 num_digits = prec + 1;
132 }
133 } else if (num.u < 0x3f800000) { // f < 1.0
134 // Build negative exponent
135 for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
136 if (*neg_pow > num.f) {
137 e += e1;
138 num.f *= *pos_pow;
139 }
140 }
Dave Hylandsb2178692015-04-12 00:36:00 -0700141 char first_dig = '0';
142 char e_sign_char = '-';
Dave Hylandsca5a2412014-03-10 00:10:01 -0700143 if (num.f < 1.0F && num.f >= 0.9999995F) {
144 num.f = 1.0F;
Dave Hylands9d6128a2015-08-31 15:43:31 -0700145 if (e > 1) {
146 // numbers less than 1.0 start with 0.xxx
147 first_dig = '1';
148 }
Dave Hylandsb2178692015-04-12 00:36:00 -0700149 if (e == 0) {
150 e_sign_char = '+';
151 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700152 } else {
153 e++;
154 num.f *= 10.0F;
155 }
156
157 // If the user specified 'g' format, and e is <= 4, then we'll switch
158 // to the fixed format ('f')
159
160 if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
161 fmt = 'f';
162 dec = -1;
Dave Hylandsb2178692015-04-12 00:36:00 -0700163 *s++ = first_dig;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700164
165 if (prec + e + 1 > buf_remaining) {
166 prec = buf_remaining - e - 1;
167 }
168
169 if (org_fmt == 'g') {
170 prec += (e - 1);
171 }
172 num_digits = prec;
173 if (num_digits) {
174 *s++ = '.';
175 while (--e && num_digits) {
176 *s++ = '0';
177 num_digits--;
178 }
179 }
180 } else {
181 // For e & g formats, we'll be printing the exponent, so set the
182 // sign.
Dave Hylandsb2178692015-04-12 00:36:00 -0700183 e_sign = e_sign_char;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700184 dec = 0;
185
186 if (prec > (buf_remaining - 6)) {
187 prec = buf_remaining - 6;
188 if (fmt == 'g') {
189 prec++;
190 }
191 }
192 }
193 } else {
194 // Build positive exponent
195 for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
196 if (*pos_pow <= num.f) {
197 e += e1;
198 num.f *= *neg_pow;
199 }
200 }
201
202 // If the user specified fixed format (fmt == 'f') and e makes the
203 // number too big to fit into the available buffer, then we'll
204 // switch to the 'e' format.
205
206 if (fmt == 'f') {
207 if (e >= buf_remaining) {
208 fmt = 'e';
209 } else if ((e + prec + 2) > buf_remaining) {
210 prec = buf_remaining - e - 2;
211 if (prec < 0) {
212 // This means no decimal point, so we can add one back
213 // for the decimal.
214 prec++;
215 }
216 }
217 }
218 if (fmt == 'e' && prec > (buf_remaining - 6)) {
219 prec = buf_remaining - 6;
220 }
221 // If the user specified 'g' format, and e is < prec, then we'll switch
222 // to the fixed format.
223
224 if (fmt == 'g' && e < prec) {
225 fmt = 'f';
226 prec -= (e + 1);
227 }
228 if (fmt == 'f') {
229 dec = e;
230 num_digits = prec + e + 1;
231 } else {
232 e_sign = '+';
233 }
234 }
235 if (prec < 0) {
236 // This can happen when the prec is trimmed to prevent buffer overflow
237 prec = 0;
238 }
239
240 // We now have num.f as a floating point number between >= 1 and < 10
241 // (or equal to zero), and e contains the absolute value of the power of
242 // 10 exponent. and (dec + 1) == the number of dgits before the decimal.
243
244 // For e, prec is # digits after the decimal
245 // For f, prec is # digits after the decimal
246 // For g, prec is the max number of significant digits
247 //
248 // For e & g there will be a single digit before the decimal
249 // for f there will be e digits before the decimal
250
251 if (fmt == 'e') {
252 num_digits = prec + 1;
253 } else if (fmt == 'g') {
254 if (prec == 0) {
255 prec = 1;
256 }
257 num_digits = prec;
258 }
259
260 // Print the digits of the mantissa
261 for (int i = 0; i < num_digits; ++i, --dec) {
262 int32_t d = num.f;
263 *s++ = '0' + d;
264 if (dec == 0 && prec > 0) {
265 *s++ = '.';
266 }
267 num.f -= (float)d;
268 num.f *= 10.0F;
269 }
270
271 // Round
272 if (num.f >= 5.0F) {
273 char *rs = s;
274 rs--;
275 while (1) {
276 if (*rs == '.') {
277 rs--;
278 continue;
279 }
280 if (*rs < '0' || *rs > '9') {
281 // + or -
282 rs++; // So we sit on the digit to the right of the sign
283 break;
284 }
285 if (*rs < '9') {
286 (*rs)++;
287 break;
288 }
289 *rs = '0';
290 if (rs == buf) {
291 break;
292 }
293 rs--;
294 }
295 if (*rs == '0') {
296 // We need to insert a 1
297 if (rs[1] == '.' && fmt != 'f') {
298 // We're going to round 9.99 to 10.00
299 // Move the decimal point
300 rs[0] = '.';
301 rs[1] = '0';
302 if (e_sign == '-') {
303 e--;
304 } else {
305 e++;
306 }
307 }
308 s++;
309 char *ss = s;
310 while (ss > rs) {
311 *ss = ss[-1];
312 ss--;
313 }
314 *rs = '1';
315 }
316 if (num.u < 0x3f800000 && fmt == 'f') {
317 // We rounded up to 1.0
318 prec--;
319 }
320 }
321
322 if (org_fmt == 'g' && prec > 0) {
323 // Remove trailing zeros and a trailing decimal point
324 while (s[-1] == '0') {
325 s--;
326 }
327 if (s[-1] == '.') {
328 s--;
329 }
330 }
331 // Append the exponent
332 if (e_sign) {
333 *s++ = e_char;
334 *s++ = e_sign;
335 *s++ = '0' + (e / 10);
336 *s++ = '0' + (e % 10);
337 }
338 *s = '\0';
339
340 return s - buf;
341}
342
stijn861670b2015-05-16 10:54:19 +0200343#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
344
345#include <errno.h>
346#include <stdio.h>
347
348#ifdef _MSC_VER
349// For msvc we need to address some quirks in the snprintf implementation:
350// - there is no standard snprintf, it is named _snprintf instead
351// - 'F' format isn't handled so use 'f'
352// - nan and inf are printed as 1.#QNAN and 1.#INF
353#include <math.h>
354#include <string.h>
355
356STATIC int copy_with_sign(char *dest, size_t bufSize, const char *value, char sign) {
357 if (bufSize == 0) {
358 return 0;
359 }
360 size_t numSignChars = 0;
361 if (sign) {
362 *dest = sign;
363 ++numSignChars;
364 }
365 // check total length including terminator
366 size_t length = strlen(value) + 1 + numSignChars;
367 if (length > bufSize) {
368 length = bufSize;
369 }
370 // length without terminator
371 --length;
372 if (length > numSignChars) {
373 memcpy(dest + numSignChars, value, length - numSignChars);
374 }
375 dest[length] = 0;
376 return length;
377}
378
379#define snprintf _snprintf
380#endif
381
382int mp_format_float(double value, char *buf, size_t bufSize, char fmt, int prec, char sign) {
383 if (!buf) {
384 errno = EINVAL;
385 return -1;
386 }
387#ifdef _MSC_VER
388 if (isnan(value)) {
389 return copy_with_sign(buf, bufSize, "nan", sign);
390 } else if (isinf(value)) {
391 return copy_with_sign(buf, bufSize, "inf", value > 0.0 ? sign : '-');
392 } else {
393 if (fmt == 'F') {
394 fmt = 'f';
395 }
396#endif
397 char fmt_buf[6];
398 char *fmt_s = fmt_buf;
399
400 *fmt_s++ = '%';
401 if (sign) {
402 *fmt_s++ = sign;
403 }
404 *fmt_s++ = '.';
405 *fmt_s++ = '*';
406 *fmt_s++ = fmt;
407 *fmt_s = '\0';
408
409 return snprintf(buf, bufSize, fmt_buf, prec, value);
410#ifdef _MSC_VER
411 }
412#endif
413}
414
Dave Hylandsca5a2412014-03-10 00:10:01 -0700415#endif