Skip to content

Commit 9aaccd4

Browse files
committed
py/formatfloat: Convert to fully portable implementation.
This takes previous IEEE-754 single precision float implementation, and converts it to fully portable parametrizable implementation using C99 functions like signbit(), isnan(), isinf(). As long as those functions are available (they can be defined in adhoc manner of course), and compiler can perform standard arithmetic and comparison operations on a float type, this implementation will work with any underlying float type (including types whose mantissa is larger than available intergral integer type).
1 parent 1818da2 commit 9aaccd4

1 file changed

Lines changed: 75 additions & 108 deletions

File tree

py/formatfloat.c

Lines changed: 75 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@
2424
* THE SOFTWARE.
2525
*/
2626

27+
#include "py/mpconfig.h"
28+
#if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE
29+
2730
#include <stdlib.h>
2831
#include <stdint.h>
2932
#include "py/formatfloat.h"
3033

31-
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
3234
/***********************************************************************
3335
34-
Routine for converting a single-precision floating
36+
Routine for converting a arbitrary floating
3537
point number into a string.
3638
3739
The code in this funcion was inspired from Fred Bayer's pdouble.c.
@@ -44,32 +46,67 @@
4446
4547
***********************************************************************/
4648

49+
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
4750
// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
4851
// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
4952
// exponent is stored with a bias of 127.
5053
// The min and max floats are on the order of 1x10^37 and 1x10^-37
5154

55+
#define FPTYPE float
56+
#define FPCONST(x) x##F
57+
#define FPROUND_TO_ONE 0.9999995F
58+
#define FPDECEXP 32
59+
5260
#define FLT_SIGN_MASK 0x80000000
5361
#define FLT_EXP_MASK 0x7F800000
5462
#define FLT_MAN_MASK 0x007FFFFF
5563

56-
static const float g_pos_pow[] = {
64+
union floatbits {
65+
float f;
66+
uint32_t u;
67+
};
68+
static inline int fp_signbit(float x) { union floatbits fb = {x}; return fb.u & FLT_SIGN_MASK; }
69+
static inline int fp_isspecial(float x) { union floatbits fb = {x}; return (fb.u & FLT_EXP_MASK) == FLT_EXP_MASK; }
70+
static inline int fp_isinf(float x) { union floatbits fb = {x}; return (fb.u & FLT_MAN_MASK) == 0; }
71+
static inline int fp_iszero(float x) { union floatbits fb = {x}; return fb.u == 0; }
72+
static inline int fp_isless1(float x) { union floatbits fb = {x}; return fb.u < 0x3f800000; }
73+
// Assumes both fp_isspecial() and fp_isinf() were applied before
74+
#define fp_isnan(x) 1
75+
76+
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
77+
78+
#define FPTYPE double
79+
#define FPCONST(x) x
80+
#define FPROUND_TO_ONE 0.999999999995
81+
#define FPDECEXP 256
82+
#include <math.h>
83+
#define fp_signbit(x) signbit(x)
84+
#define fp_isspecial(x) 1
85+
#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+
92+
static const FPTYPE g_pos_pow[] = {
93+
#if FPDECEXP > 32
94+
1e256, 1e128, 1e64,
95+
#endif
5796
1e32, 1e16, 1e8, 1e4, 1e2, 1e1
5897
};
59-
static const float g_neg_pow[] = {
98+
static const FPTYPE g_neg_pow[] = {
99+
#if FPDECEXP > 32
100+
1e-256, 1e-128, 1e-64,
101+
#endif
60102
1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
61103
};
62104

63-
int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
105+
int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
64106

65107
char *s = buf;
66108
int buf_remaining = buf_size - 1;
67109

68-
union {
69-
float f;
70-
uint32_t u;
71-
} num = {f};
72-
73110
if (buf_size < 7) {
74111
// Smallest exp notion is -9e+99 which is 6 chars plus terminating
75112
// null.
@@ -82,29 +119,31 @@ int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, cha
82119
}
83120
return buf_size >= 2;
84121
}
85-
if (num.u & FLT_SIGN_MASK) {
122+
if (fp_signbit(f)) {
86123
*s++ = '-';
87-
num.u &= ~FLT_SIGN_MASK;
124+
f = -f;
88125
} else {
89126
if (sign) {
90127
*s++ = sign;
91128
}
92129
}
93130
buf_remaining -= (s - buf); // Adjust for sign
94131

95-
if ((num.u & FLT_EXP_MASK) == FLT_EXP_MASK) {
132+
if (fp_isspecial(f)) {
96133
char uc = fmt & 0x20;
97-
if ((num.u & FLT_MAN_MASK) == 0) {
134+
if (fp_isinf(f)) {
98135
*s++ = 'I' ^ uc;
99136
*s++ = 'N' ^ uc;
100137
*s++ = 'F' ^ uc;
101-
} else {
138+
goto ret;
139+
} else if (fp_isnan(f)) {
102140
*s++ = 'N' ^ uc;
103141
*s++ = 'A' ^ uc;
104142
*s++ = 'N' ^ uc;
143+
ret:
144+
*s = '\0';
145+
return s - buf;
105146
}
106-
*s = '\0';
107-
return s - buf;
108147
}
109148

110149
if (prec < 0) {
@@ -120,28 +159,28 @@ int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, cha
120159
int dec = 0;
121160
char e_sign = '\0';
122161
int num_digits = 0;
123-
const float *pos_pow = g_pos_pow;
124-
const float *neg_pow = g_neg_pow;
162+
const FPTYPE *pos_pow = g_pos_pow;
163+
const FPTYPE *neg_pow = g_neg_pow;
125164

126-
if (num.u == 0) {
165+
if (fp_iszero(f)) {
127166
e = 0;
128167
if (fmt == 'e') {
129168
e_sign = '+';
130169
} else if (fmt == 'f') {
131170
num_digits = prec + 1;
132171
}
133-
} else if (num.u < 0x3f800000) { // f < 1.0
172+
} else if (fp_isless1(f)) {
134173
// Build negative exponent
135-
for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
136-
if (*neg_pow > num.f) {
174+
for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
175+
if (*neg_pow > f) {
137176
e += e1;
138-
num.f *= *pos_pow;
177+
f *= *pos_pow;
139178
}
140179
}
141180
char first_dig = '0';
142181
char e_sign_char = '-';
143-
if (num.f < 1.0F && num.f >= 0.9999995F) {
144-
num.f = 1.0F;
182+
if (fp_isless1(f) && f >= FPROUND_TO_ONE) {
183+
f = FPCONST(1.0);
145184
if (e > 1) {
146185
// numbers less than 1.0 start with 0.xxx
147186
first_dig = '1';
@@ -151,7 +190,7 @@ int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, cha
151190
}
152191
} else {
153192
e++;
154-
num.f *= 10.0F;
193+
f *= FPCONST(10.0);
155194
}
156195

157196
// If the user specified 'g' format, and e is <= 4, then we'll switch
@@ -192,10 +231,10 @@ int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, cha
192231
}
193232
} else {
194233
// Build positive exponent
195-
for (e = 0, e1 = 32; e1; e1 >>= 1, pos_pow++, neg_pow++) {
196-
if (*pos_pow <= num.f) {
234+
for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
235+
if (*pos_pow <= f) {
197236
e += e1;
198-
num.f *= *neg_pow;
237+
f *= *neg_pow;
199238
}
200239
}
201240

@@ -259,17 +298,17 @@ int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, cha
259298

260299
// Print the digits of the mantissa
261300
for (int i = 0; i < num_digits; ++i, --dec) {
262-
int32_t d = num.f;
301+
int32_t d = f;
263302
*s++ = '0' + d;
264303
if (dec == 0 && prec > 0) {
265304
*s++ = '.';
266305
}
267-
num.f -= (float)d;
268-
num.f *= 10.0F;
306+
f -= (FPTYPE)d;
307+
f *= FPCONST(10.0);
269308
}
270309

271310
// Round
272-
if (num.f >= 5.0F) {
311+
if (f >= FPCONST(5.0)) {
273312
char *rs = s;
274313
rs--;
275314
while (1) {
@@ -313,7 +352,7 @@ int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, cha
313352
}
314353
*rs = '1';
315354
}
316-
if (num.u < 0x3f800000 && fmt == 'f') {
355+
if (fp_isless1(f) && fmt == 'f') {
317356
// We rounded up to 1.0
318357
prec--;
319358
}
@@ -340,76 +379,4 @@ int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, cha
340379
return s - buf;
341380
}
342381

343-
#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-
356-
STATIC 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-
382-
int 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-
415-
#endif
382+
#endif // MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE

0 commit comments

Comments
 (0)