Files
Core2forAWS-MicroPython/py/formatfloat.c
T

426 lines
12 KiB
C
Raw Normal View History

2014-05-03 23:27:38 +01:00
/*
* This file is part of the MicroPython project, http://micropython.org/
2014-05-03 23:27:38 +01:00
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include "py/formatfloat.h"
/***********************************************************************
Routine for converting a arbitrary floating
point number into a string.
2014-03-10 00:10:01 -07:00
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.
2014-03-10 00:10:01 -07:00
The original code can be found in https://github.com/dhylands/format-float
2014-03-10 00:10:01 -07:00
Dave Hylands
2014-03-10 00:10:01 -07:00
***********************************************************************/
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
2014-03-10 00:10:01 -07:00
// 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 FPTYPE float
#define FPCONST(x) x##F
#define FPROUND_TO_ONE 0.9999995F
#define FPDECEXP 32
#define FPMIN_BUF_SIZE 6 // +9e+99
2014-03-10 00:10:01 -07:00
#define FLT_SIGN_MASK 0x80000000
#define FLT_EXP_MASK 0x7F800000
#define FLT_MAN_MASK 0x007FFFFF
union floatbits {
float f;
uint32_t u;
};
static inline int fp_signbit(float x) { union floatbits fb = {x}; return fb.u & FLT_SIGN_MASK; }
#define fp_isnan(x) isnan(x)
#define fp_isinf(x) isinf(x)
static inline int fp_iszero(float x) { union floatbits fb = {x}; return fb.u == 0; }
static inline int fp_isless1(float x) { union floatbits fb = {x}; return fb.u < 0x3f800000; }
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
#define FPTYPE double
#define FPCONST(x) x
#define FPROUND_TO_ONE 0.999999999995
#define FPDECEXP 256
#define FPMIN_BUF_SIZE 7 // +9e+199
#define fp_signbit(x) signbit(x)
#define fp_isnan(x) isnan(x)
#define fp_isinf(x) isinf(x)
#define fp_iszero(x) (x == 0)
#define fp_isless1(x) (x < 1.0)
#endif
static const FPTYPE g_pos_pow[] = {
#if FPDECEXP > 32
1e256, 1e128, 1e64,
#endif
2014-03-10 00:10:01 -07:00
1e32, 1e16, 1e8, 1e4, 1e2, 1e1
};
static const FPTYPE g_neg_pow[] = {
#if FPDECEXP > 32
1e-256, 1e-128, 1e-64,
#endif
2014-03-10 00:10:01 -07:00
1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
};
int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
2014-03-10 00:10:01 -07:00
char *s = buf;
if (buf_size <= FPMIN_BUF_SIZE) {
// FPMIN_BUF_SIZE is the minimum size needed to store any FP number.
// If the buffer does not have enough room for this (plus null terminator)
// then don't try to format the float.
2014-03-10 00:10:01 -07:00
if (buf_size >= 2) {
*s++ = '?';
}
if (buf_size >= 1) {
*s = '\0';
2014-03-10 00:10:01 -07:00
}
return buf_size >= 2;
}
if (fp_signbit(f) && !fp_isnan(f)) {
2014-03-10 00:10:01 -07:00
*s++ = '-';
f = -f;
2014-03-10 00:10:01 -07:00
} else {
if (sign) {
*s++ = sign;
}
}
// buf_remaining contains bytes available for digits and exponent.
// It is buf_size minus room for the sign and null byte.
int buf_remaining = buf_size - 1 - (s - buf);
2014-03-10 00:10:01 -07:00
{
2014-03-10 00:10:01 -07:00
char uc = fmt & 0x20;
if (fp_isinf(f)) {
2014-03-10 00:10:01 -07:00
*s++ = 'I' ^ uc;
*s++ = 'N' ^ uc;
*s++ = 'F' ^ uc;
goto ret;
} else if (fp_isnan(f)) {
2014-03-10 00:10:01 -07:00
*s++ = 'N' ^ uc;
*s++ = 'A' ^ uc;
*s++ = 'N' ^ uc;
ret:
*s = '\0';
return s - buf;
2014-03-10 00:10:01 -07:00
}
}
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;
2014-03-10 00:10:01 -07:00
int dec = 0;
char e_sign = '\0';
int num_digits = 0;
const FPTYPE *pos_pow = g_pos_pow;
const FPTYPE *neg_pow = g_neg_pow;
2014-03-10 00:10:01 -07:00
if (fp_iszero(f)) {
2014-03-10 00:10:01 -07:00
e = 0;
if (fmt == 'f') {
// Truncate precision to prevent buffer overflow
if (prec + 2 > buf_remaining) {
prec = buf_remaining - 2;
}
2014-03-10 00:10:01 -07:00
num_digits = prec + 1;
} else {
// Truncate precision to prevent buffer overflow
if (prec + 6 > buf_remaining) {
prec = buf_remaining - 6;
}
if (fmt == 'e') {
e_sign = '+';
}
2014-03-10 00:10:01 -07:00
}
} else if (fp_isless1(f)) {
// We need to figure out what an integer digit will be used
// in case 'f' is used (or we revert other format to it below).
// As we just tested number to be <1, this is obviously 0,
// but we can round it up to 1 below.
char first_dig = '0';
if (f >= FPROUND_TO_ONE) {
first_dig = '1';
}
2014-03-10 00:10:01 -07:00
// Build negative exponent
for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
if (*neg_pow > f) {
2014-03-10 00:10:01 -07:00
e += e1;
f *= *pos_pow;
2014-03-10 00:10:01 -07:00
}
}
char e_sign_char = '-';
if (fp_isless1(f) && f >= FPROUND_TO_ONE) {
f = FPCONST(1.0);
if (e == 0) {
e_sign_char = '+';
}
} else if (fp_isless1(f)) {
e++;
f *= FPCONST(10.0);
2014-03-10 00:10:01 -07:00
}
// 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++ = first_dig;
2014-03-10 00:10:01 -07:00
if (org_fmt == 'g') {
prec += (e - 1);
}
// truncate precision to prevent buffer overflow
if (prec + 2 > buf_remaining) {
prec = buf_remaining - 2;
}
2014-03-10 00:10:01 -07:00
num_digits = prec;
if (num_digits) {
*s++ = '.';
2014-03-10 00:10:01 -07:00
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 = e_sign_char;
2014-03-10 00:10:01 -07:00
dec = 0;
if (prec > (buf_remaining - FPMIN_BUF_SIZE)) {
prec = buf_remaining - FPMIN_BUF_SIZE;
2014-03-10 00:10:01 -07:00
if (fmt == 'g') {
prec++;
}
}
}
} else {
// Build positive exponent
for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
if (*pos_pow <= f) {
2014-03-10 00:10:01 -07:00
e += e1;
f *= *neg_pow;
2014-03-10 00:10:01 -07:00
}
}
// It can be that f was right on the edge of an entry in pos_pow needs to be reduced
if (f >= FPCONST(10.0)) {
e += 1;
f *= FPCONST(0.1);
}
// If the user specified fixed format (fmt == 'f') and e makes the
2014-03-10 00:10:01 -07:00
// 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 - FPMIN_BUF_SIZE)) {
prec = buf_remaining - FPMIN_BUF_SIZE;
2014-03-10 00:10:01 -07:00
}
if (fmt == 'g'){
// Truncate precision to prevent buffer overflow
if (prec + (FPMIN_BUF_SIZE - 1) > buf_remaining) {
prec = buf_remaining - (FPMIN_BUF_SIZE - 1);
}
}
2014-03-10 00:10:01 -07:00
// 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;
2014-03-10 00:10:01 -07:00
}
// Print the digits of the mantissa
for (int i = 0; i < num_digits; ++i, --dec) {
int32_t d = (int32_t)f;
2014-03-10 00:10:01 -07:00
*s++ = '0' + d;
if (dec == 0 && prec > 0) {
*s++ = '.';
}
f -= (FPTYPE)d;
f *= FPCONST(10.0);
2014-03-10 00:10:01 -07:00
}
// Round
// If we print non-exponential format (i.e. 'f'), but a digit we're going
// to round by (e) is too far away, then there's nothing to round.
if ((org_fmt != 'f' || e <= 1) && f >= FPCONST(5.0)) {
2014-03-10 00:10:01 -07:00
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--;
2014-03-10 00:10:01 -07:00
}
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--;
if (e == 0) {
e_sign = '+';
}
2014-03-10 00:10:01 -07:00
} else {
e++;
2014-03-10 00:10:01 -07:00
}
} else {
// Need at extra digit at the end to make room for the leading '1'
s++;
2014-03-10 00:10:01 -07:00
}
char *ss = s;
2014-03-10 00:10:01 -07:00
while (ss > rs) {
*ss = ss[-1];
ss--;
}
*rs = '1';
}
}
// verify that we did not overrun the input buffer so far
assert((size_t)(s + 1 - buf) <= buf_size);
2014-03-10 00:10:01 -07:00
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;
if (FPMIN_BUF_SIZE == 7 && e >= 100) {
*s++ = '0' + (e / 100);
}
*s++ = '0' + ((e / 10) % 10);
2014-03-10 00:10:01 -07:00
*s++ = '0' + (e % 10);
}
*s = '\0';
// verify that we did not overrun the input buffer
assert((size_t)(s + 1 - buf) <= buf_size);
2014-03-10 00:10:01 -07:00
return s - buf;
}
#endif // MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE