Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
TASTE
uPython-mirror
Commits
ca5a241e
Commit
ca5a241e
authored
Mar 10, 2014
by
Dave Hylands
Browse files
Add proper floating point printing support.
parent
0308f964
Changes
6
Hide whitespace changes
Inline
Side-by-side
py/format-float.c
0 → 100644
View file @
ca5a241e
/***********************************************************************
format-float.c - Ruutine for converting a single-precision floating
point number into a string.
The code in this funcion was inspired from Fred Bayer's pdouble.c.
Since pdouble.c was released as Public Domain, I'm releasing this
code as public domain as well.
The original code can be found in https://github.com/dhylands/format-float
Dave Hylands
***********************************************************************/
#include
<stdint.h>
#include
<stdlib.h>
#include
"mpconfig.h"
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
#include
"format-float.h"
// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
// exponent is stored with a bias of 127.
// The min and max floats are on the order of 1x10^37 and 1x10^-37
#define FLT_SIGN_MASK 0x80000000
#define FLT_EXP_MASK 0x7F800000
#define FLT_MAN_MASK 0x007FFFFF
static
const
float
g_pos_pow
[]
=
{
1e32
,
1e16
,
1e8
,
1e4
,
1e2
,
1e1
};
static
const
float
g_neg_pow
[]
=
{
1e-32
,
1e-16
,
1e-8
,
1e-4
,
1e-2
,
1e-1
};
int
format_float
(
float
f
,
char
*
buf
,
size_t
buf_size
,
char
fmt
,
int
prec
,
char
sign
)
{
char
*
s
=
buf
;
int
buf_remaining
=
buf_size
-
1
;
union
{
float
f
;
uint32_t
u
;
}
num
=
{
f
};
if
(
buf_size
<
7
)
{
// Smallest exp notion is -9e+99 which is 6 chars plus terminating
// nulll.
if
(
buf_size
>=
2
)
{
*
s
++
=
'?'
;
}
if
(
buf_size
>=
1
)
{
*
s
++
=
'\0'
;
}
return
buf_size
>=
2
;
}
if
(
num
.
u
&
FLT_SIGN_MASK
)
{
*
s
++
=
'-'
;
num
.
u
&=
~
FLT_SIGN_MASK
;
}
else
{
if
(
sign
)
{
*
s
++
=
sign
;
}
}
buf_remaining
-=
(
s
-
buf
);
// Adjust for sign
if
((
num
.
u
&
FLT_EXP_MASK
)
==
FLT_EXP_MASK
)
{
char
uc
=
fmt
&
0x20
;
if
((
num
.
u
&
FLT_MAN_MASK
)
==
0
)
{
*
s
++
=
'I'
^
uc
;
*
s
++
=
'N'
^
uc
;
*
s
++
=
'F'
^
uc
;
}
else
{
*
s
++
=
'N'
^
uc
;
*
s
++
=
'A'
^
uc
;
*
s
++
=
'N'
^
uc
;
}
*
s
=
'\0'
;
return
s
-
buf
;
}
if
(
prec
<
0
)
{
prec
=
6
;
}
char
e_char
=
'E'
|
(
fmt
&
0x20
);
// e_char will match case of fmt
fmt
|=
0x20
;
// Force fmt to be lowercase
char
org_fmt
=
fmt
;
if
(
fmt
==
'g'
&&
prec
==
0
)
{
prec
=
1
;
}
int
e
,
e1
;
int
dec
=
0
;
char
e_sign
=
'\0'
;
int
num_digits
=
0
;
const
float
*
pos_pow
=
g_pos_pow
;
const
float
*
neg_pow
=
g_neg_pow
;
if
(
num
.
u
==
0
)
{
e
=
0
;
if
(
fmt
==
'e'
)
{
e_sign
=
'+'
;
}
else
if
(
fmt
==
'f'
)
{
num_digits
=
prec
+
1
;
}
}
else
if
(
num
.
u
<
0x3f800000
)
{
// f < 1.0
// Build negative exponent
for
(
e
=
0
,
e1
=
32
;
e1
;
e1
>>=
1
,
pos_pow
++
,
neg_pow
++
)
{
if
(
*
neg_pow
>
num
.
f
)
{
e
+=
e1
;
num
.
f
*=
*
pos_pow
;
}
}
if
(
num
.
f
<
1
.
0
F
&&
num
.
f
>=
0
.
9999995
F
)
{
num
.
f
=
1
.
0
F
;
}
else
{
e
++
;
num
.
f
*=
10
.
0
F
;
}
// If the user specified 'g' format, and e is <= 4, then we'll switch
// to the fixed format ('f')
if
(
fmt
==
'f'
||
(
fmt
==
'g'
&&
e
<=
4
))
{
fmt
=
'f'
;
dec
=
-
1
;
*
s
++
=
'0'
;
if
(
prec
+
e
+
1
>
buf_remaining
)
{
prec
=
buf_remaining
-
e
-
1
;
}
if
(
org_fmt
==
'g'
)
{
prec
+=
(
e
-
1
);
}
num_digits
=
prec
;
if
(
num_digits
)
{
*
s
++
=
'.'
;
while
(
--
e
&&
num_digits
)
{
*
s
++
=
'0'
;
num_digits
--
;
}
}
}
else
{
// For e & g formats, we'll be printing the exponent, so set the
// sign.
e_sign
=
'-'
;
dec
=
0
;
if
(
prec
>
(
buf_remaining
-
6
))
{
prec
=
buf_remaining
-
6
;
if
(
fmt
==
'g'
)
{
prec
++
;
}
}
}
}
else
{
// Build positive exponent
for
(
e
=
0
,
e1
=
32
;
e1
;
e1
>>=
1
,
pos_pow
++
,
neg_pow
++
)
{
if
(
*
pos_pow
<=
num
.
f
)
{
e
+=
e1
;
num
.
f
*=
*
neg_pow
;
}
}
// If the user specified fixed format (fmt == 'f') and e makes the
// number too big to fit into the available buffer, then we'll
// switch to the 'e' format.
if
(
fmt
==
'f'
)
{
if
(
e
>=
buf_remaining
)
{
fmt
=
'e'
;
}
else
if
((
e
+
prec
+
2
)
>
buf_remaining
)
{
prec
=
buf_remaining
-
e
-
2
;
if
(
prec
<
0
)
{
// This means no decimal point, so we can add one back
// for the decimal.
prec
++
;
}
}
}
if
(
fmt
==
'e'
&&
prec
>
(
buf_remaining
-
6
))
{
prec
=
buf_remaining
-
6
;
}
// If the user specified 'g' format, and e is < prec, then we'll switch
// to the fixed format.
if
(
fmt
==
'g'
&&
e
<
prec
)
{
fmt
=
'f'
;
prec
-=
(
e
+
1
);
}
if
(
fmt
==
'f'
)
{
dec
=
e
;
num_digits
=
prec
+
e
+
1
;
}
else
{
e_sign
=
'+'
;
}
}
if
(
prec
<
0
)
{
// This can happen when the prec is trimmed to prevent buffer overflow
prec
=
0
;
}
// We now have num.f as a floating point number between >= 1 and < 10
// (or equal to zero), and e contains the absolute value of the power of
// 10 exponent. and (dec + 1) == the number of dgits before the decimal.
// For e, prec is # digits after the decimal
// For f, prec is # digits after the decimal
// For g, prec is the max number of significant digits
//
// For e & g there will be a single digit before the decimal
// for f there will be e digits before the decimal
if
(
fmt
==
'e'
)
{
num_digits
=
prec
+
1
;
}
else
if
(
fmt
==
'g'
)
{
if
(
prec
==
0
)
{
prec
=
1
;
}
num_digits
=
prec
;
}
// Print the digits of the mantissa
for
(
int
i
=
0
;
i
<
num_digits
;
++
i
,
--
dec
)
{
int32_t
d
=
num
.
f
;
*
s
++
=
'0'
+
d
;
if
(
dec
==
0
&&
prec
>
0
)
{
*
s
++
=
'.'
;
}
num
.
f
-=
(
float
)
d
;
num
.
f
*=
10
.
0
F
;
}
// Round
if
(
num
.
f
>=
5
.
0
F
)
{
char
*
rs
=
s
;
rs
--
;
while
(
1
)
{
if
(
*
rs
==
'.'
)
{
rs
--
;
continue
;
}
if
(
*
rs
<
'0'
||
*
rs
>
'9'
)
{
// + or -
rs
++
;
// So we sit on the digit to the right of the sign
break
;
}
if
(
*
rs
<
'9'
)
{
(
*
rs
)
++
;
break
;
}
*
rs
=
'0'
;
if
(
rs
==
buf
)
{
break
;
}
rs
--
;
}
if
(
*
rs
==
'0'
)
{
// We need to insert a 1
if
(
rs
[
1
]
==
'.'
&&
fmt
!=
'f'
)
{
// We're going to round 9.99 to 10.00
// Move the decimal point
rs
[
0
]
=
'.'
;
rs
[
1
]
=
'0'
;
if
(
e_sign
==
'-'
)
{
e
--
;
}
else
{
e
++
;
}
}
s
++
;
char
*
ss
=
s
;
while
(
ss
>
rs
)
{
*
ss
=
ss
[
-
1
];
ss
--
;
}
*
rs
=
'1'
;
}
if
(
num
.
u
<
0x3f800000
&&
fmt
==
'f'
)
{
// We rounded up to 1.0
prec
--
;
}
}
if
(
org_fmt
==
'g'
&&
prec
>
0
)
{
// Remove trailing zeros and a trailing decimal point
while
(
s
[
-
1
]
==
'0'
)
{
s
--
;
}
if
(
s
[
-
1
]
==
'.'
)
{
s
--
;
}
}
// Append the exponent
if
(
e_sign
)
{
*
s
++
=
e_char
;
*
s
++
=
e_sign
;
*
s
++
=
'0'
+
(
e
/
10
);
*
s
++
=
'0'
+
(
e
%
10
);
}
*
s
=
'\0'
;
return
s
-
buf
;
}
#endif
py/format-float.h
0 → 100644
View file @
ca5a241e
int
format_float
(
float
f
,
char
*
buf
,
size_t
bufSize
,
char
fmt
,
int
prec
,
char
sign
);
py/objfloat.c
View file @
ca5a241e
...
...
@@ -12,12 +12,21 @@
#include
"runtime0.h"
#if MICROPY_ENABLE_FLOAT
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
#include
"format-float.h"
#endif
mp_obj_t
mp_obj_new_float
(
mp_float_t
value
);
STATIC
void
float_print
(
void
(
*
print
)(
void
*
env
,
const
char
*
fmt
,
...),
void
*
env
,
mp_obj_t
o_in
,
mp_print_kind_t
kind
)
{
mp_obj_float_t
*
o
=
o_in
;
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
char
buf
[
32
];
format_float
(
o
->
value
,
buf
,
sizeof
(
buf
),
'g'
,
6
,
'\0'
);
print
(
env
,
"%s"
,
buf
);
#else
print
(
env
,
"%.8g"
,
(
double
)
o
->
value
);
#endif
}
STATIC
mp_obj_t
float_make_new
(
mp_obj_t
type_in
,
uint
n_args
,
uint
n_kw
,
const
mp_obj_t
*
args
)
{
...
...
py/py.mk
View file @
ca5a241e
...
...
@@ -32,6 +32,7 @@ PY_O_BASENAME = \
asmthumb.o
\
emitnthumb.o
\
emitinlinethumb.o
\
format-float.o
\
parsenumbase.o
\
parsenum.o
\
runtime.o
\
...
...
stm/mpconfigport.h
View file @
ca5a241e
...
...
@@ -7,6 +7,7 @@
#define MICROPY_ENABLE_GC (1)
#define MICROPY_ENABLE_REPL_HELPERS (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_ENABLE_FLOAT (1)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_PATH_MAX (128)
/* Enable FatFS LFNs
...
...
stm/printf.c
View file @
ca5a241e
...
...
@@ -11,6 +11,9 @@
#include
"lcd.h"
#include
"usart.h"
#include
"usb.h"
#if MICROPY_ENABLE_FLOAT
#include
"format-float.h"
#endif
#define PF_FLAG_LEFT_ADJUST (0x01)
#define PF_FLAG_SHOW_SIGN (0x02)
...
...
@@ -210,29 +213,40 @@ int pfenv_printf(const pfenv_t *pfenv, const char *fmt, va_list args) {
chrs
+=
pfenv_print_int
(
pfenv
,
va_arg
(
args
,
int
),
0
,
16
,
'A'
,
flags
,
width
);
break
;
#if MICROPY_ENABLE_FLOAT
case
'e'
:
case
'E'
:
case
'f'
:
case
'F'
:
case
'g'
:
case
'G'
:
{
// This is a very hacky approach to printing floats. Micropython
// uses %g when using print, and I just wanted to see somthing
// usable. I expect that this will be replaced with something
// more appropriate.
char
dot
=
'.'
;
mp_float_t
d
=
va_arg
(
args
,
double
);
int
left
=
(
int
)
d
;
int
right
=
(
int
)((
d
-
(
mp_float_t
)(
int
)
d
)
*
1000000
.
0
);
if
(
right
<
0
)
{
if
(
left
==
0
)
{
chrs
+=
pfenv_print_strn
(
pfenv
,
"-0"
,
2
,
flags
,
width
);
}
else
{
chrs
+=
pfenv_print_int
(
pfenv
,
left
,
1
,
10
,
'a'
,
flags
,
width
);
}
chrs
+=
pfenv_print_strn
(
pfenv
,
&
dot
,
1
,
flags
,
width
);
chrs
+=
pfenv_print_int
(
pfenv
,
-
right
,
0
,
10
,
'a'
,
PF_FLAG_ZERO_PAD
,
6
);
}
else
{
chrs
+=
pfenv_print_int
(
pfenv
,
left
,
1
,
10
,
'a'
,
flags
,
width
);
chrs
+=
pfenv_print_strn
(
pfenv
,
&
dot
,
1
,
flags
,
width
);
chrs
+=
pfenv_print_int
(
pfenv
,
right
,
0
,
10
,
'a'
,
PF_FLAG_ZERO_PAD
,
6
);
char
buf
[
32
];
char
sign
=
'\0'
;
if
(
flags
&
PF_FLAG_SHOW_SIGN
)
{
sign
=
'+'
;
}
else
if
(
flags
&
PF_FLAG_SPACE_SIGN
)
{
sign
=
' '
;
}
float
f
=
va_arg
(
args
,
double
);
int
len
=
format_float
(
f
,
buf
,
sizeof
(
buf
),
*
fmt
,
prec
,
sign
);
char
*
s
=
buf
;
// buf[0] < '0' returns true if the first character is space, + or -
// buf[1] < '9' matches a digit, and doesn't match when we get back +nan or +inf
if
(
buf
[
0
]
<
'0'
&&
buf
[
1
]
<=
'9'
&&
(
flags
&
PF_FLAG_ZERO_PAD
))
{
chrs
+=
pfenv_print_strn
(
pfenv
,
&
buf
[
0
],
1
,
0
,
1
);
s
++
;
width
--
;
len
--
;
}
if
(
*
s
<
'0'
||
*
s
>=
'9'
)
{
// For inf or nan, we don't want to zero pad.
flags
&=
~
PF_FLAG_ZERO_PAD
;
}
chrs
+=
pfenv_print_strn
(
pfenv
,
s
,
len
,
flags
,
width
);
break
;
}
#endif
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment