summaryrefslogtreecommitdiff
path: root/newlib/libm/test/dcvt.c
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libm/test/dcvt.c')
-rw-r--r--newlib/libm/test/dcvt.c605
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);
+}