/* smalldtoa.c (emx+gcc) -- Copyright (c) 1994-1996 by Eberhard Mattes */

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <emx/float.h>


/* Convert a positive floating point number to a string of decimal
   digits and an exponent.  The position of the decimal point is
   assumed to be after the first digit.  Examples:

   1234567890123456789 --> 1234567890123456789, xp = 18
   123                 --> 1230000000000000000, xp =  2
   12                  --> 1200000000000000000, xp =  1
   1.2                 --> 1200000000000000000, xp =  0
   0.12                --> 1200000000000000000, xp = -1

   This function assumes that

     LDBL_MANT_DIG <= 8 * sizeof (unsigned long long)

   which is true for the extended real format (64-bit mantissa).
   There are algorithms which are more precise, but they're more
   complicated. */

char *__small_dtoa (char *buffer, int *p_exp,
                    long double x, int ndigits, int fmt, int dig)
{
  long double scaled;
  unsigned long long intg;
  int xp, len, a, i, next;

  xp = (int)_floorl (_log10l (x));

  if (LDBL_DIG - 1 - xp < LDBL_MAX_10_EXP)
    {
      /* This is the normal case. */

      scaled = x * _powl (10.0, LDBL_DIG - 1 - xp);
    }
  else
    {
      /* Avoid overflow in _powl() for X near LDBL_MIN. */

      scaled = (_powl (10.0, LDBL_DIG - 1 - xp - (LDBL_MAX_10_EXP - 1))
                * (x * _powl (10.0, LDBL_MAX_10_EXP - 1)));
    }

  intg = (unsigned long long)scaled;
  buffer[0] = '0';
  _ulltoa (intg, buffer + 1, 10);

  switch (fmt)
    {
    case DTOA_PRINTF_E:
      a = ndigits + 1;
      break;
    case DTOA_PRINTF_F:
      if (xp >= 0)
        a = xp + 1 + ndigits;
      else
        a = ndigits;
      break;
    case DTOA_PRINTF_G:
    case DTOA_GCVT:
      a = ndigits;
      break;
    default:
      abort ();
    }
  if (a > dig)
    a = dig;

  len = strlen (buffer + 1);
  xp += len - LDBL_DIG;
  if (len > a)
    {
      /* Round. */

      next = 0;
      if (buffer[a+1] > '5')
        next = 1;
      else if (buffer[a+1] == '5')
        {
          i = a + 2;
          while (buffer[i] == '0')
            ++i;
          next = (buffer[i] != 0 || (buffer[a] & 1));
        }
      if (next)
        while (++buffer[a] > '9')
          --a;
    }

  i = a + 1;
  while (i < DECIMAL_DIG + 1)
    buffer[i++] = '0';
  buffer[i] = 0;

  if (buffer[0] != '0')
    {
      *p_exp = xp + 1;
      return buffer;
    }
  else
    {
      *p_exp = xp;
      return buffer + 1;
    }
}
