blob: 64f0131471143872082c6ad71018af2b38eb630c [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 }
Dave Hylandsb2178692015-04-12 00:36:00 -0700145 char first_dig = '0';
146 char e_sign_char = '-';
Dave Hylandsca5a2412014-03-10 00:10:01 -0700147 if (num.f < 1.0F && num.f >= 0.9999995F) {
148 num.f = 1.0F;
Dave Hylandsb2178692015-04-12 00:36:00 -0700149 first_dig = '1';
150 if (e == 0) {
151 e_sign_char = '+';
152 }
Dave Hylandsca5a2412014-03-10 00:10:01 -0700153 } else {
154 e++;
155 num.f *= 10.0F;
156 }
157
158 // If the user specified 'g' format, and e is <= 4, then we'll switch
159 // to the fixed format ('f')
160
161 if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
162 fmt = 'f';
163 dec = -1;
Dave Hylandsb2178692015-04-12 00:36:00 -0700164 *s++ = first_dig;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700165
166 if (prec + e + 1 > buf_remaining) {
167 prec = buf_remaining - e - 1;
168 }
169
170 if (org_fmt == 'g') {
171 prec += (e - 1);
172 }
173 num_digits = prec;
174 if (num_digits) {
175 *s++ = '.';
176 while (--e && num_digits) {
177 *s++ = '0';
178 num_digits--;
179 }
180 }
181 } else {
182 // For e & g formats, we'll be printing the exponent, so set the
183 // sign.
Dave Hylandsb2178692015-04-12 00:36:00 -0700184 e_sign = e_sign_char;
Dave Hylandsca5a2412014-03-10 00:10:01 -0700185 dec = 0;
186
187 if (prec > (buf_remaining - 6)) {
188 prec = buf_remaining - 6;
189 if (fmt == 'g') {
190 prec++;
191 }
192 }
193 }
194 } else {
195 // Build positive exponent
196 for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
197 if (*pos_pow <= num.f) {
198 e += e1;
199 num.f *= *neg_pow;
200 }
201 }
202
203 // If the user specified fixed format (fmt == 'f') and e makes the
204 // number too big to fit into the available buffer, then we'll
205 // switch to the 'e' format.
206
207 if (fmt == 'f') {
208 if (e >= buf_remaining) {
209 fmt = 'e';
210 } else if ((e + prec + 2) > buf_remaining) {
211 prec = buf_remaining - e - 2;
212 if (prec < 0) {
213 // This means no decimal point, so we can add one back
214 // for the decimal.
215 prec++;
216 }
217 }
218 }
219 if (fmt == 'e' && prec > (buf_remaining - 6)) {
220 prec = buf_remaining - 6;
221 }
222 // If the user specified 'g' format, and e is < prec, then we'll switch
223 // to the fixed format.
224
225 if (fmt == 'g' && e < prec) {
226 fmt = 'f';
227 prec -= (e + 1);
228 }
229 if (fmt == 'f') {
230 dec = e;
231 num_digits = prec + e + 1;
232 } else {
233 e_sign = '+';
234 }
235 }
236 if (prec < 0) {
237 // This can happen when the prec is trimmed to prevent buffer overflow
238 prec = 0;
239 }
240
241 // We now have num.f as a floating point number between >= 1 and < 10
242 // (or equal to zero), and e contains the absolute value of the power of
243 // 10 exponent. and (dec + 1) == the number of dgits before the decimal.
244
245 // For e, prec is # digits after the decimal
246 // For f, prec is # digits after the decimal
247 // For g, prec is the max number of significant digits
248 //
249 // For e & g there will be a single digit before the decimal
250 // for f there will be e digits before the decimal
251
252 if (fmt == 'e') {
253 num_digits = prec + 1;
254 } else if (fmt == 'g') {
255 if (prec == 0) {
256 prec = 1;
257 }
258 num_digits = prec;
259 }
260
261 // Print the digits of the mantissa
262 for (int i = 0; i < num_digits; ++i, --dec) {
263 int32_t d = num.f;
264 *s++ = '0' + d;
265 if (dec == 0 && prec > 0) {
266 *s++ = '.';
267 }
268 num.f -= (float)d;
269 num.f *= 10.0F;
270 }
271
272 // Round
273 if (num.f >= 5.0F) {
274 char *rs = s;
275 rs--;
276 while (1) {
277 if (*rs == '.') {
278 rs--;
279 continue;
280 }
281 if (*rs < '0' || *rs > '9') {
282 // + or -
283 rs++; // So we sit on the digit to the right of the sign
284 break;
285 }
286 if (*rs < '9') {
287 (*rs)++;
288 break;
289 }
290 *rs = '0';
291 if (rs == buf) {
292 break;
293 }
294 rs--;
295 }
296 if (*rs == '0') {
297 // We need to insert a 1
298 if (rs[1] == '.' && fmt != 'f') {
299 // We're going to round 9.99 to 10.00
300 // Move the decimal point
301 rs[0] = '.';
302 rs[1] = '0';
303 if (e_sign == '-') {
304 e--;
305 } else {
306 e++;
307 }
308 }
309 s++;
310 char *ss = s;
311 while (ss > rs) {
312 *ss = ss[-1];
313 ss--;
314 }
315 *rs = '1';
316 }
317 if (num.u < 0x3f800000 && fmt == 'f') {
318 // We rounded up to 1.0
319 prec--;
320 }
321 }
322
323 if (org_fmt == 'g' && prec > 0) {
324 // Remove trailing zeros and a trailing decimal point
325 while (s[-1] == '0') {
326 s--;
327 }
328 if (s[-1] == '.') {
329 s--;
330 }
331 }
332 // Append the exponent
333 if (e_sign) {
334 *s++ = e_char;
335 *s++ = e_sign;
336 *s++ = '0' + (e / 10);
337 *s++ = '0' + (e % 10);
338 }
339 *s = '\0';
340
341 return s - buf;
342}
343
344#endif