blob: cc06361008574af888c8dc673d7d2bd85ace0bef [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 Hylandsb2178692015-04-12 00:36:00 -0700145 first_dig = '1';
146 if (e == 0) {
147 e_sign_char = '+';
148 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700149 } else {
150 e++;
151 num.f *= 10.0F;
152 }
153
154 // If the user specified 'g' format, and e is <= 4, then we'll switch
155 // to the fixed format ('f')
156
157 if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
158 fmt = 'f';
159 dec = -1;
Dave Hylandsb2178692015-04-12 00:36:00 -0700160 *s++ = first_dig;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700161
162 if (prec + e + 1 > buf_remaining) {
163 prec = buf_remaining - e - 1;
164 }
165
166 if (org_fmt == 'g') {
167 prec += (e - 1);
168 }
169 num_digits = prec;
170 if (num_digits) {
171 *s++ = '.';
172 while (--e && num_digits) {
173 *s++ = '0';
174 num_digits--;
175 }
176 }
177 } else {
178 // For e & g formats, we'll be printing the exponent, so set the
179 // sign.
Dave Hylandsb2178692015-04-12 00:36:00 -0700180 e_sign = e_sign_char;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700181 dec = 0;
182
183 if (prec > (buf_remaining - 6)) {
184 prec = buf_remaining - 6;
185 if (fmt == 'g') {
186 prec++;
187 }
188 }
189 }
190 } else {
191 // Build positive exponent
192 for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
193 if (*pos_pow <= num.f) {
194 e += e1;
195 num.f *= *neg_pow;
196 }
197 }
198
199 // If the user specified fixed format (fmt == 'f') and e makes the
200 // number too big to fit into the available buffer, then we'll
201 // switch to the 'e' format.
202
203 if (fmt == 'f') {
204 if (e >= buf_remaining) {
205 fmt = 'e';
206 } else if ((e + prec + 2) > buf_remaining) {
207 prec = buf_remaining - e - 2;
208 if (prec < 0) {
209 // This means no decimal point, so we can add one back
210 // for the decimal.
211 prec++;
212 }
213 }
214 }
215 if (fmt == 'e' && prec > (buf_remaining - 6)) {
216 prec = buf_remaining - 6;
217 }
218 // If the user specified 'g' format, and e is < prec, then we'll switch
219 // to the fixed format.
220
221 if (fmt == 'g' && e < prec) {
222 fmt = 'f';
223 prec -= (e + 1);
224 }
225 if (fmt == 'f') {
226 dec = e;
227 num_digits = prec + e + 1;
228 } else {
229 e_sign = '+';
230 }
231 }
232 if (prec < 0) {
233 // This can happen when the prec is trimmed to prevent buffer overflow
234 prec = 0;
235 }
236
237 // We now have num.f as a floating point number between >= 1 and < 10
238 // (or equal to zero), and e contains the absolute value of the power of
239 // 10 exponent. and (dec + 1) == the number of dgits before the decimal.
240
241 // For e, prec is # digits after the decimal
242 // For f, prec is # digits after the decimal
243 // For g, prec is the max number of significant digits
244 //
245 // For e & g there will be a single digit before the decimal
246 // for f there will be e digits before the decimal
247
248 if (fmt == 'e') {
249 num_digits = prec + 1;
250 } else if (fmt == 'g') {
251 if (prec == 0) {
252 prec = 1;
253 }
254 num_digits = prec;
255 }
256
257 // Print the digits of the mantissa
258 for (int i = 0; i < num_digits; ++i, --dec) {
259 int32_t d = num.f;
260 *s++ = '0' + d;
261 if (dec == 0 && prec > 0) {
262 *s++ = '.';
263 }
264 num.f -= (float)d;
265 num.f *= 10.0F;
266 }
267
268 // Round
269 if (num.f >= 5.0F) {
270 char *rs = s;
271 rs--;
272 while (1) {
273 if (*rs == '.') {
274 rs--;
275 continue;
276 }
277 if (*rs < '0' || *rs > '9') {
278 // + or -
279 rs++; // So we sit on the digit to the right of the sign
280 break;
281 }
282 if (*rs < '9') {
283 (*rs)++;
284 break;
285 }
286 *rs = '0';
287 if (rs == buf) {
288 break;
289 }
290 rs--;
291 }
292 if (*rs == '0') {
293 // We need to insert a 1
294 if (rs[1] == '.' && fmt != 'f') {
295 // We're going to round 9.99 to 10.00
296 // Move the decimal point
297 rs[0] = '.';
298 rs[1] = '0';
299 if (e_sign == '-') {
300 e--;
301 } else {
302 e++;
303 }
304 }
305 s++;
306 char *ss = s;
307 while (ss > rs) {
308 *ss = ss[-1];
309 ss--;
310 }
311 *rs = '1';
312 }
313 if (num.u < 0x3f800000 && fmt == 'f') {
314 // We rounded up to 1.0
315 prec--;
316 }
317 }
318
319 if (org_fmt == 'g' && prec > 0) {
320 // Remove trailing zeros and a trailing decimal point
321 while (s[-1] == '0') {
322 s--;
323 }
324 if (s[-1] == '.') {
325 s--;
326 }
327 }
328 // Append the exponent
329 if (e_sign) {
330 *s++ = e_char;
331 *s++ = e_sign;
332 *s++ = '0' + (e / 10);
333 *s++ = '0' + (e % 10);
334 }
335 *s = '\0';
336
337 return s - buf;
338}
339
stijn861670b2015-05-16 10:54:19 +0200340#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
341
342#include <errno.h>
343#include <stdio.h>
344
345#ifdef _MSC_VER
346// For msvc we need to address some quirks in the snprintf implementation:
347// - there is no standard snprintf, it is named _snprintf instead
348// - 'F' format isn't handled so use 'f'
349// - nan and inf are printed as 1.#QNAN and 1.#INF
350#include <math.h>
351#include <string.h>
352
353STATIC int copy_with_sign(char *dest, size_t bufSize, const char *value, char sign) {
354 if (bufSize == 0) {
355 return 0;
356 }
357 size_t numSignChars = 0;
358 if (sign) {
359 *dest = sign;
360 ++numSignChars;
361 }
362 // check total length including terminator
363 size_t length = strlen(value) + 1 + numSignChars;
364 if (length > bufSize) {
365 length = bufSize;
366 }
367 // length without terminator
368 --length;
369 if (length > numSignChars) {
370 memcpy(dest + numSignChars, value, length - numSignChars);
371 }
372 dest[length] = 0;
373 return length;
374}
375
376#define snprintf _snprintf
377#endif
378
379int mp_format_float(double value, char *buf, size_t bufSize, char fmt, int prec, char sign) {
380 if (!buf) {
381 errno = EINVAL;
382 return -1;
383 }
384#ifdef _MSC_VER
385 if (isnan(value)) {
386 return copy_with_sign(buf, bufSize, "nan", sign);
387 } else if (isinf(value)) {
388 return copy_with_sign(buf, bufSize, "inf", value > 0.0 ? sign : '-');
389 } else {
390 if (fmt == 'F') {
391 fmt = 'f';
392 }
393#endif
394 char fmt_buf[6];
395 char *fmt_s = fmt_buf;
396
397 *fmt_s++ = '%';
398 if (sign) {
399 *fmt_s++ = sign;
400 }
401 *fmt_s++ = '.';
402 *fmt_s++ = '*';
403 *fmt_s++ = fmt;
404 *fmt_s = '\0';
405
406 return snprintf(buf, bufSize, fmt_buf, prec, value);
407#ifdef _MSC_VER
408 }
409#endif
410}
411
Dave Hylandsca5a2412014-03-10 00:10:01 -0700412#endif