diff options
| author | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
|---|---|---|
| committer | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
| commit | 268a53de823a6750d6256ee1fb1e7707b4b45740 (patch) | |
| tree | 42c1799a9a82b2f7d9790ee9fe181d72a7274751 /device/lib/pic14/libc/_ftoa.c | |
| download | sdcc-gas-268a53de823a6750d6256ee1fb1e7707b4b45740.tar.gz | |
sdcc-3.9.0 fork implementing GNU assembler syntax
This fork aims to provide better support for stm8-binutils
Diffstat (limited to 'device/lib/pic14/libc/_ftoa.c')
| -rw-r--r-- | device/lib/pic14/libc/_ftoa.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/device/lib/pic14/libc/_ftoa.c b/device/lib/pic14/libc/_ftoa.c new file mode 100644 index 0000000..4baf176 --- /dev/null +++ b/device/lib/pic14/libc/_ftoa.c @@ -0,0 +1,308 @@ +/*------------------------------------------------------------------------- + x_ftoa.c - wrapper function to use _convert_float + + Copyright (C) 2004, Vangelis Rokas <vrokas at otenet.gr> + + Modifications for PIC14 by + Copyright (C) 2019 Gonzalo Pérez de Olaguer Córdoba <salo@gpoc.es> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + + As a special exception, if you link this library with other files, + some of which are compiled with SDCC, to produce an executable, + this library does not by itself cause the resulting executable to + be covered by the GNU General Public License. This exception does + not however invalidate any other reasons why the executable file + might be covered by the GNU General Public License. +-------------------------------------------------------------------------*/ + +#define __SDCC_FLOAT_LIB +#include <float.h> +#include <stdlib.h> + +union { + unsigned long l; + float f; +} lf; +int e2; +int e10; + +#define M lf.l /* mantissa */ +#define F lf.f /* float */ +#define E2 e2 /* exponent base 2 */ +#define E10 e10 /* exponent base 10 */ + +/* While processing decimal output we consider the mantissa as a + * fixed point number with the integer part in bits 31:28 and the + * fractional part in bits 27:0. + */ +#define MANT_UNIT (1UL << 28) + +static char *S; /* the output string */ + +static void _div2 (void) +{ + M = ((M + 1) >> 1); +} + +static void _mul2 (void) +{ + M = (M << 1); +} + +static void _div5 (void) +{ + M = ((M + 2) / 5); +} + +static void _mul5 (void) +{ + M = (M + (M << 2)); +} + +static void _div10 (void) +{ + M = ((M + 5) / 10); +} + +static void _mul10 (void) +{ + M = ((M + (M << 2)) << 1); +} + +static void _round (int x) +{ + if (x >= 0) + { + unsigned long r = M; + M = (MANT_UNIT >> 1); + while (x > 0) + { + _div10(); + --x; + } + M += r; + if (M >= 10 * MANT_UNIT) + { + _div10(); + ++E10; + } + } +} + +static void _put_char (char c) +{ + *S++ = c; +} + +static void _put_digit (void) +{ + _put_char( '0' + ((M >> 28) & 0x0f)); + M &= 0x0fffffffUL; + _mul10(); +} + +void _ftoa (float value, char *str, unsigned char prec) +{ + F = value; + S = str; + + /* output sign */ + if (SIGN(M)) + _put_char('-'); + + E2 = EXP(M); + M = MANT(M); + + /* infinity or not a number */ + if (E2 == 255) + { + if (M == HIDDEN) + { + /* infinity */ + _put_char('i'); + _put_char('n'); + _put_char('F'); + } + else + { + /* not a number */ + _put_char('n'); + _put_char('a'); + _put_char('n'); + } + _put_char('\0'); + return; + } + + /* zero or denormal number */ + if (E2 == 0) + { + if (M == HIDDEN) + { + /* zero */ + _put_char('0'); + E10 = prec & PREC_P; + if (E10) + { + _put_char('.'); + while (E10--) + _put_char('0'); + } + if (prec & PREC_E) + { + _put_char('E2'); + _put_char('+'); + _put_char('0'); + _put_char('0'); + } + _put_char('\0'); + return; + } + else + { + /* denormal number: value = (M - HIDDEN) * pow (2, - 149) */ + M &= ~HIDDEN; + E2 = 1; + while (M < HIDDEN) + { + _mul2(); + --E2; + } + } + } + + /* Now we have a normalized number with value = M * pow (2, E2 - 150) + * where (1 << 23) <= M < (1 << 24) and -22 <= E2 < 255 + * + * We adjust it to make M a base2 fixed point value with the implicit + * decimal point at bit 28 (so bits 31:28 are the integer part and + * bits 27:0 are the fractional part) and E2 is the unbiased base2 + * exponent. + * + * Then we convert it to M * pow (10, E10) + */ + M <<= (28-23); /* now MANT_UNIT <= M < 2 * MANT_UNIT */ + E2 -= EXCESS + 1; /* now -149 <= E2 < 128 */ + E10 = 0; /* base 10 exponent */ + while (E2 > 0) + { + if (M < 5 * MANT_UNIT) + { + _mul2(); + } + else + { + _div5(); + ++E10; + } + --E2; + } + while (E2 < 0) + { + if (M < 2 * MANT_UNIT) + { + _mul5(); + --E10; + } + else + { + _div2(); + } + ++E2; + } + while (M < MANT_UNIT) + { + _mul10(); + --E10; + } + /* Now we have a number with value = M * pow (10, E10) + * where 1 <= M < 10 with fixed point after bit 28. + */ + /* Format G: use format E10 if exp < -4 or exp >= prec; format F otherwise. */ + if ((prec & (PREC_E|PREC_F)) == 0) + { + if (E10 < -4 || E10 >= prec) + prec |= PREC_E; + else + prec |= PREC_F; + } + /* Format E: [-]d.dddE+-dd rounded to 'prec' fractional digits (prec >= 0) */ + if (prec & PREC_E) + { + _round(prec & PREC_P); + _put_digit(); + E2 = prec & PREC_P; + if (E2 > 0) + { + _put_char('.'); + while (E2 > 0) + { + _put_digit(); + --E2; + } + } + _put_char('E'); + if (E10 < 0) + { + _put_char('-'); + E10 = - E10; + } + else + { + _put_char('+'); + } + _uitoa (E10, S, 10); + } + /* Format F: [-]ddd.ddd rounded to 'prec' fractional digits (prec >= 0) */ + else + { + _round(E10 + (prec & PREC_P)); + /* Print the integer part */ + if (E10 >= 0) + { + while (E10 >= 0) + { + _put_digit(); + --E10; + } + } + else + { + _put_char('0'); + } + E2 = prec & PREC_P; + if (E2 > 0) + { + /* Print the fractional part */ + _put_char('.'); + while (E2 > 0) + { + if (E10 < -1) + { + _put_char('0'); + ++E10; + } + else + { + _put_digit(); + } + --E2; + } + } + _put_char('\0'); + } +} |
