Skip to content

Commit 861670b

Browse files
stinosdpgeorge
authored andcommitted
py: Implement mp_format_float for doubles and use where appropriate
This allows using (almost) the same code for printing floats everywhere, removes the dependency on sprintf and uses just snprintf and applies an msvc-specific fix for snprintf in a single place so nan/inf are now printed correctly.
1 parent f5dd6f7 commit 861670b

8 files changed

Lines changed: 97 additions & 112 deletions

File tree

py/formatfloat.c

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,15 @@
2424
* THE SOFTWARE.
2525
*/
2626

27+
#include <stdlib.h>
28+
#include <stdint.h>
29+
#include "py/formatfloat.h"
30+
31+
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
2732
/***********************************************************************
2833
29-
formatfloat.c - Ruutine for converting a single-precision floating
30-
point number into a string.
34+
Routine for converting a single-precision floating
35+
point number into a string.
3136
3237
The code in this funcion was inspired from Fred Bayer's pdouble.c.
3338
Since pdouble.c was released as Public Domain, I'm releasing this
@@ -39,15 +44,6 @@
3944
4045
***********************************************************************/
4146

42-
#include <stdlib.h>
43-
#include <stdint.h>
44-
45-
#include "py/mpconfig.h"
46-
47-
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
48-
49-
#include "py/formatfloat.h"
50-
5147
// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
5248
// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
5349
// exponent is stored with a bias of 127.
@@ -341,4 +337,76 @@ int mp_format_float(float f, char *buf, size_t buf_size, char fmt, int prec, cha
341337
return s - buf;
342338
}
343339

340+
#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+
353+
STATIC 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+
379+
int 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+
344412
#endif

py/formatfloat.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
#ifndef __MICROPY_INCLUDED_PY_FORMATFLOAT_H__
2727
#define __MICROPY_INCLUDED_PY_FORMATFLOAT_H__
2828

29-
int mp_format_float(float f, char *buf, size_t bufSize, char fmt, int prec, char sign);
29+
#include "py/mpconfig.h"
30+
31+
#if MICROPY_PY_BUILTINS_FLOAT
32+
int mp_format_float(mp_float_t f, char *buf, size_t bufSize, char fmt, int prec, char sign);
33+
#endif
3034

3135
#endif // __MICROPY_INCLUDED_PY_FORMATFLOAT_H__

py/mpprint.c

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@
3535
#include "py/objint.h"
3636
#include "py/runtime.h"
3737

38-
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
39-
#include <stdio.h>
40-
#endif
41-
4238
#if MICROPY_PY_BUILTINS_FLOAT
4339
#include "py/formatfloat.h"
4440
#endif
@@ -340,29 +336,12 @@ int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, c
340336
if (flags & PF_FLAG_SPACE_SIGN) {
341337
sign = ' ';
342338
}
343-
int len;
344-
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
345-
len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign);
346-
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
347-
char fmt_buf[6];
348-
char *fmt_s = fmt_buf;
349339

350-
*fmt_s++ = '%';
351-
if (sign) {
352-
*fmt_s++ = sign;
353-
}
354-
*fmt_s++ = '.';
355-
*fmt_s++ = '*';
356-
*fmt_s++ = fmt;
357-
*fmt_s = '\0';
358-
359-
len = snprintf(buf, sizeof(buf), fmt_buf, prec, f);
340+
int len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign);
360341
if (len < 0) {
361342
len = 0;
362343
}
363-
#else
364-
#error Unknown MICROPY FLOAT IMPL
365-
#endif
344+
366345
char *s = buf;
367346

368347
if ((flags & PF_FLAG_ADD_PERCENT) && (size_t)(len + 1) < sizeof(buf)) {

py/objcomplex.c

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@
3737
#if MICROPY_PY_BUILTINS_COMPLEX
3838

3939
#include <math.h>
40-
41-
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
4240
#include "py/formatfloat.h"
43-
#endif
4441

4542
typedef struct _mp_obj_complex_t {
4643
mp_obj_base_t base;
@@ -53,33 +50,23 @@ STATIC void complex_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_
5350
mp_obj_complex_t *o = o_in;
5451
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
5552
char buf[16];
56-
if (o->real == 0) {
57-
mp_format_float(o->imag, buf, sizeof(buf), 'g', 7, '\0');
58-
mp_printf(print, "%sj", buf);
59-
} else {
60-
mp_format_float(o->real, buf, sizeof(buf), 'g', 7, '\0');
61-
mp_printf(print, "(%s", buf);
62-
if (o->imag >= 0 || isnan(o->imag)) {
63-
mp_print_str(print, "+");
64-
}
65-
mp_format_float(o->imag, buf, sizeof(buf), 'g', 7, '\0');
66-
mp_printf(print, "%sj)", buf);
67-
}
53+
const int precision = 7;
6854
#else
6955
char buf[32];
56+
const int precision = 16;
57+
#endif
7058
if (o->real == 0) {
71-
sprintf(buf, "%.16g", (double)o->imag);
59+
mp_format_float(o->imag, buf, sizeof(buf), 'g', precision, '\0');
7260
mp_printf(print, "%sj", buf);
7361
} else {
74-
sprintf(buf, "%.16g", (double)o->real);
62+
mp_format_float(o->real, buf, sizeof(buf), 'g', precision, '\0');
7563
mp_printf(print, "(%s", buf);
7664
if (o->imag >= 0 || isnan(o->imag)) {
7765
mp_print_str(print, "+");
7866
}
79-
sprintf(buf, "%.16g", (double)o->imag);
67+
mp_format_float(o->imag, buf, sizeof(buf), 'g', precision, '\0');
8068
mp_printf(print, "%sj)", buf);
8169
}
82-
#endif
8370
}
8471

8572
STATIC mp_obj_t complex_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {

py/objfloat.c

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,24 @@
3737
#if MICROPY_PY_BUILTINS_FLOAT
3838

3939
#include <math.h>
40-
41-
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
4240
#include "py/formatfloat.h"
43-
#endif
4441

4542
STATIC void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
4643
(void)kind;
4744
mp_obj_float_t *o = o_in;
4845
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
4946
char buf[16];
50-
mp_format_float(o->value, buf, sizeof(buf), 'g', 7, '\0');
51-
mp_print_str(print, buf);
52-
if (strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) {
53-
// Python floats always have decimal point (unless inf or nan)
54-
mp_print_str(print, ".0");
55-
}
47+
const int precision = 7;
5648
#else
5749
char buf[32];
58-
sprintf(buf, "%.16g", (double) o->value);
50+
const int precision = 16;
51+
#endif
52+
mp_format_float(o->value, buf, sizeof(buf), 'g', precision, '\0');
5953
mp_print_str(print, buf);
6054
if (strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) {
6155
// Python floats always have decimal point (unless inf or nan)
6256
mp_print_str(print, ".0");
6357
}
64-
#endif
6558
}
6659

6760
STATIC mp_obj_t float_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {

tests/float/float1.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
print(float("1e+1"))
1414
print(float("1e-1"))
1515
print(float("inf"))
16+
print(float("-inf"))
1617
print(float("INF"))
1718
print(float("infinity"))
1819
print(float("INFINITY"))

windows/mpconfigport.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,4 @@ void msec_sleep(double msec);
198198
#include <stddef.h> //for NULL
199199
#include <assert.h> //for assert
200200

201-
// Functions implemented in platform code
202-
203-
int snprintf(char *dest, size_t count, const char *format, ...);
204201
#endif

windows/msvc/snprintf.c

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)