diff options
Diffstat (limited to 'newlib/libm/test/dcvt.c')
-rw-r--r-- | newlib/libm/test/dcvt.c | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/newlib/libm/test/dcvt.c b/newlib/libm/test/dcvt.c new file mode 100644 index 00000000000..2aaa1af28ad --- /dev/null +++ b/newlib/libm/test/dcvt.c @@ -0,0 +1,605 @@ + + +#include <limits.h> +#include <math.h> +#include <stdio.h> +#include <float.h> +#include <ieeefp.h> +#include <stdlib.h> +#include <string.h> +#define _MAX_CHARS 512 + +static char *lcset = "0123456789abcdef"; + +static struct p { + double pvalue, nvalue; + int exp; +} powers[] = +{ +{ 1e32, 1e-32, 32}, +{ 1e16, 1e-16, 16}, +{ 1e8, 1e-8, 8}, +{ 1e4, 1e-4, 4}, +{ 1e2, 1e-2, 2}, +{ 1e1, 1e-1, 1 }, +{ 1e0, 1e-0, 0 } +}; + +#define _MAX_PREC 16 + +static char +_DEFUN(nextdigit,(value), +double *value) +{ + double tmp; + + *value = modf (*value * 10, &tmp) ; + return lcset[(int)tmp]; +} + + +static char * +_DEFUN(print_nan,(buffer, value, precision), + char *buffer _AND + double value _AND + int precision) +{ + size_t i; + + if (isnan(value)) + { + strcpy(buffer, "nan"); + i = 3; + + } + else + { + strcpy(buffer, "infinity"); + i = 8; + } + + while (i < precision) + { + buffer[i++] = ' '; + } + buffer[i++] = 0; + return buffer; + +} + +/* A convert info struct */ +typedef struct +{ + char *buffer ; /* Destination of conversion */ + double value; /* scratch Value to convert */ + double original_value; /* saved Value to convert */ + int value_neg; /* OUT: 1 if value initialiy neg */ + int abs_exp; /* abs Decimal exponent of value */ + int abs_exp_sign; /* + or - */ + int exp; /* exp not sgned */ + int type; /* fFeEgG used in printing before exp */ + + int print_trailing_zeros; /* Print 00's after a . */ + + int null_idx; /* Index of the null at the end */ + +/* These ones are read only */ + int decimal_places; /* the number of digits to print after + the decimal */ + int max_digits; /* total number of digits to print */ + int buffer_size; /* Size of output buffer */ + + /* Two sorts of dot ness. + 0 never ever print a dot + 1 print a dot if followed by a digit + 2 always print a dot, even if no digit following + */ + enum { dot_never, dot_sometimes, dot_always} dot; /* Print a decimal point, always */ + int dot_idx; /* where the dot went, or would have gone */ +} cvt_info_type; + + +void +_DEFUN(renormalize,(in), + cvt_info_type *in) +{ + + /* Make sure all numbers are less than 1 */ + + while (in->value >= 1.0) + { + in->value = in->value * 0.1; + in->exp++; + } + + /* Now we have only numbers between 0 and .9999.., and have adjusted + exp to account for the shift */ + + if (in->exp >= 0) + { + in->abs_exp_sign = '+'; + in->abs_exp = in->exp; + } + else + { + in->abs_exp_sign = '-'; + in->abs_exp = -in->exp; + } + +} + +/* This routine looks at original_value, and makes it between 0 and 1, + modifying exp as it goes + */ + +static void +_DEFUN(normalize,(value, in), + double value _AND + cvt_info_type *in) +{ + int j; + int texp; + if (value != 0) + { + texp = -1; + + + if (value < 0.0) + { + in->value_neg =1 ; + value = - value; + } + else + { + in->value_neg = 0; + } + + + /* Work out texponent & normalise value */ + + /* If value > 1, then shrink it */ + if (value >= 1.0) + { + for (j = 0; j < 6; j++) + { + while (value >= powers[j].pvalue) + { + value /= powers[j].pvalue; + texp += powers[j].exp; + } + } + } + else if (value != 0.0) + { + for (j = 0; j < 6; j++) + { + while (value <= powers[j].nvalue) + { + value *= powers[j].pvalue; + texp -= powers[j].exp; + } + } + } + } + + else + { + texp = 0; + } + + + in->exp = texp; + in->value = value; + in->original_value = value; + renormalize(in); + +} +int +_DEFUN(round,(in, start, now, ch), + cvt_info_type *in _AND + char *start _AND + char *now _AND + char ch) +{ + double rounder = 5.0; + + char *p; + int ok = 0; + + now --; + + /* If the next digit to output would have been a '5' run back and */ + /* see if we can create a more rounded number. If we can then do it. + If not (like when the number was 9.9 and the last char was + another 9), then we'll have to modify the number and try again */ + if (ch < '5') + return 0; + + + for (p = now;!ok && p >= start; p--) + { + switch (*p) + { + default: + abort(); + case '.': + break; + case '9': + rounder = rounder * 0.1; + break; + case '8': + case '7': + case '6': + case '5': + case '4': + case '3': + case '2': + case '1': + case '0': + p = now; + while (1) { + if (*p == '9') { + *p = '0'; + } + else if (*p != '.') { + (*p)++; + return 0; + } + p--; + } + } + + } + + /* Getting here means that we couldn't round the number in place + textually - there have been all nines. + We'll have to add to it and try the conversion again + eg + .99999[9] can't be rounded in place, so add + .000005 to it giving: + 1.000004 we notice that the result is > 1 so add to exp and + divide by 10 + .100004 + */ + + in->original_value = in->value = in->original_value + rounder; + normalize(in->original_value , in); + return 1; + + +} + + + +void +_DEFUN(_cvte,(in), + register cvt_info_type *in) +{ + int buffer_idx =0; + int digit = 0; + + int after_decimal =0; + + in->buffer[buffer_idx++] = nextdigit(&(in->value)); + digit++; + in->dot_idx = buffer_idx; + + + switch (in->dot) + { + case dot_never: + break; + case dot_sometimes: + if (in->decimal_places + && digit < in->max_digits) + { + in->buffer[buffer_idx++] = '.'; + } + break; + case dot_always: + in->buffer[buffer_idx++] = '.'; + } + + + while (buffer_idx < in->buffer_size + && after_decimal < in->decimal_places + && digit < in->max_digits) + { + in->buffer[buffer_idx] = nextdigit(&(in->value)); + after_decimal++; + buffer_idx++; + digit++; + + } + + if (round(in, + in->buffer, + in->buffer+buffer_idx, + nextdigit(&(in->value)))) + { + _cvte(in); + } + else + { + in->buffer[buffer_idx++] = in->type; + in->buffer[buffer_idx++] = in->abs_exp_sign; + + if (in->abs_exp >= 100) + { + in->buffer[buffer_idx++] = lcset[in->abs_exp / 100]; + in->abs_exp %= 100; + } + in->buffer[buffer_idx++] = lcset[in->abs_exp / 10]; + in->buffer[buffer_idx++] = lcset[in->abs_exp % 10]; + } + + in->buffer[buffer_idx++] = 0; +} + + + + +/* Produce NNNN.FFFF */ +void +_DEFUN(_cvtf,(in), + cvt_info_type *in) +{ + + int buffer_idx = 0; /* Current char being output */ + int after_decimal = 0; + int digit =0; + + + in->dot_idx = in->exp + 1; + + /* Two sorts of number, NNN.FFF and 0.0000...FFFF */ + + + /* Print all the digits up to the decimal point */ + + while (buffer_idx <= in->exp + && digit < in->max_digits + && buffer_idx < in->buffer_size) + { + in->buffer[buffer_idx] = nextdigit(&(in->value)); + buffer_idx++; + digit ++; + } + + + /* And the decimal point if we should */ + if (buffer_idx < in->buffer_size) + { + + switch (in->dot) + { + case dot_never: + break; + case dot_sometimes: + /* Only print a dot if following chars */ + if (in->decimal_places + && digit < in->max_digits ) + { + in->buffer[buffer_idx++] = '.'; + } + + break; + case dot_always: + in->buffer[buffer_idx++] = '.'; + } + + after_decimal = 0; + + /* And the digits following the point if necessary */ + + /* Only print the leading zeros if a dot was possible */ + if (in->dot || in->exp>0) + { + while (buffer_idx < in->buffer_size + && (in->abs_exp_sign == '-' && digit < in->abs_exp - 1) + && (after_decimal < in->decimal_places) + && (digit < in->max_digits)) + { + in->buffer[buffer_idx] = '0'; + buffer_idx++; + digit++; + after_decimal++; + } + } + + while (buffer_idx < in->buffer_size + && after_decimal < in->decimal_places + && digit < in->max_digits) + { + in->buffer[buffer_idx] = nextdigit(&(in->value)); + buffer_idx++; + digit++; + after_decimal++; + } + } + + in->null_idx = buffer_idx; + in->buffer[buffer_idx] = 0; + if (round(in, in->buffer, in->buffer+buffer_idx, + nextdigit(&(in->value)))) + { + _cvtf(in); + } + + + + +} + + + +char * +_DEFUN(_dcvt,(buffer, invalue, precision, width, type, dot), + char *buffer _AND + double invalue _AND + int precision _AND + int width _AND + char type _AND + int dot) +{ + cvt_info_type in; + + + + in.buffer = buffer; + in.buffer_size = 512; + + if (!finite(invalue)) + { + return print_nan(buffer, invalue, precision); + } + + + normalize(invalue, &in); + + in.type = type; + in.dot = dot? dot_always: dot_sometimes; + + switch (type) + { + + case 'g': + case 'G': + /* When formatting a g, the precision refers to the number of + char positions *total*, this leads to various off by ones */ + { + /* A precision of 0 means 1 */ + if (precision == 0) + precision = 1; + + /* A g turns into an e if there are more digits than the + precision, or it's smaller than e-4 */ + if (in.exp >= precision || in.exp < -4) + { + in.type = (type == 'g' ? 'e' : 'E'); + in.decimal_places = _MAX_CHARS; + in.max_digits = precision; + in.print_trailing_zeros = 1; + _cvte(&in); + } + else + { + /* G means total number of chars to print */ + in.decimal_places = _MAX_CHARS; + in.max_digits = precision; + in.type = (type == 'g' ? 'f' : 'F'); + in.print_trailing_zeros = 0; + _cvtf(&in); + + if (!dot) { + /* trim trailing zeros */ + int j = in.null_idx -1; + while (j > 0 && in.buffer[j] == '0') + { + in.buffer[j] = 0; + j--; + } + /* Stamp on a . if not followed by zeros */ + if (j > 0 && buffer[j] == '.') + in.buffer[j] = 0; + } + } + + + break; + case 'f': + case 'F': + in.decimal_places= precision; + in.max_digits = _MAX_CHARS; + in.print_trailing_zeros = 1; + _cvtf(&in); + break; + case 'e': + case 'E': + in.print_trailing_zeros = 1; + in.decimal_places = precision; + in.max_digits = _MAX_CHARS; + _cvte(&in); + break; + } + + } + + + return buffer; +} + + + + +char * +_DEFUN(fcvtbuf,(invalue,ndigit,decpt,sign, fcvt_buf), + double invalue _AND + int ndigit _AND + int *decpt _AND + int *sign _AND + char *fcvt_buf) +{ + cvt_info_type in; + in.buffer = fcvt_buf; + in.buffer_size = 512; + + if (!finite(invalue)) + { + return print_nan(fcvt_buf, invalue, ndigit); + } + + normalize(invalue, &in); + + in.dot = dot_never; /* Don't print a decimal point */ + in.max_digits = _MAX_CHARS; + in.buffer_size = _MAX_CHARS; /* Take as many as needed */ + in.decimal_places = ndigit; + _cvtf(&in); + *decpt = in.dot_idx; + *sign = in.value_neg; + return in.buffer; +} + + +char * +_DEFUN(ecvtbuf,(invalue,ndigit,decpt,sign, fcvt_buf), + double invalue _AND + int ndigit _AND + int *decpt _AND + int *sign _AND + char *fcvt_buf) +{ + cvt_info_type in; + in.buffer = fcvt_buf; + + if (!finite(invalue)) + { + return print_nan(fcvt_buf, invalue, ndigit); + } + + normalize(invalue, &in); + + + in.dot = dot_never; /* Don't print a decimal point */ +/* We can work out how many digits go after the decimal point */ + + in.buffer_size =_MAX_CHARS; + in.decimal_places = _MAX_CHARS; + in.max_digits = ndigit; /* Take as many as told */ + _cvtf(&in); + *decpt = in.dot_idx; + *sign = in.value_neg; + return in.buffer; +} + + + +char * +_DEFUN(gcvt,(d,ndigit,buf), + double d _AND + int ndigit _AND + char *buf) +{ + return _dcvt(buf, d, ndigit, 0, 'g', 1); +} |