void PrintFloat()

in renderdoc/strings/utf8printf.cpp [542:1182]


void PrintFloat(double argd, FormatterParams &formatter, bool e, bool f, bool g, bool a,
                bool uppercaseDigits, char *&output, size_t &actualsize, char *end)
{
  // extract the pieces out of the double
  uint64_t *arg64 = (uint64_t *)&argd;
  bool signbit = (*arg64 & 0x8000000000000000) ? true : false;
  uint64_t rawexp = (*arg64 & 0x7ff0000000000000) >> 52;
  int exponent = int(rawexp) - 1023;
  uint64_t mantissa = (*arg64 & 0x000fffffffffffff);

  char prepend = '\0';

  if(signbit)
    prepend = '-';
  else if(formatter.Flags & PrependPos)
    prepend = '+';
  else if(formatter.Flags & PrependSpace)
    prepend = ' ';

  // special-case handling of printing 0
  if(rawexp == 0 && mantissa == 0)
  {
    PrintFloat0(e, f, a, uppercaseDigits, formatter, prepend, output, actualsize, end);
  }
  // handle 'special' values, inf and nan
  else if(rawexp == 0x7ff)
  {
    if(mantissa == 0)
    {
      if(signbit)
        appendstring(output, actualsize, end, uppercaseDigits ? "-INF" : "-inf");
      else
        appendstring(output, actualsize, end, uppercaseDigits ? "+INF" : "+inf");
    }
    else
    {
      appendstring(output, actualsize, end, uppercaseDigits ? "NAN" : "nan");
    }
  }
  else if(a)
  {
    char digits[18] = {0};
    int ndigits = 0;

    for(int d = 12; mantissa && d >= 0; d--)
    {
      const uint64_t mask = 0xfULL << (d * 4);
      const uint64_t digit = (mantissa & mask) >> (d * 4);

      char c = char('0' + digit);
      if(digit >= 10)
        c = char((uppercaseDigits ? 'A' : 'a') + digit - 10);

      digits[ndigits++] = c;

      mantissa &= ~mask;
    }

    // if no precision is specified, drop trailing 0s and ensure we don't pad or trim
    if(formatter.Precision == FormatterParams::NoPrecision)
    {
      while(ndigits > 0 && digits[ndigits - 1] == '0')
        ndigits--;

      formatter.Precision = ndigits;
    }

    // hold the carry bit because if it's needed we have no decimals and need to format 0x2p+E
    // instead of 0x1.Mp+E
    bool carry = false;

    // see if we need to trim and round
    if(ndigits > formatter.Precision)
    {
      int removedigs = ndigits - formatter.Precision;

      // if we're removing all digits, just check the first to see if it should be
      // rounded up or down
      if(removedigs == ndigits)
      {
        ndigits = 0;
        // all hex digits are above '8' in ascii order, so we can do just one comparison
        carry = (digits[0] >= '8');
      }
      else
      {
        // remove the specified number of digits
        ndigits -= removedigs;

        // round up the last digit (continually rolling up if necessary)
        // note this will look 'ahead' into the last removed digits at first
        carry = true;
        for(int i = ndigits - 1; i >= 0; i--)
        {
          // should we round up?
          if(digits[i + 1] >= '8')
          {
            digits[i + 1] = 0;

            // unless current digit is an f, we can just increment it and stop
            if(digits[i] != 'f' && digits[i] != 'F')
            {
              if(digits[i] != '9')
                digits[i]++;
              else
                digits[i] = uppercaseDigits ? 'A' : 'a';
              carry = false;
              break;
            }

            // continue (carry to next digit)
          }
          else
          {
            // didn't need to round up, everything's fine.
            carry = false;
            break;
          }

          // trim off a digit (was an f/F)
          ndigits--;
          continue;
        }
      }
    }

    if(digits[0] == '0' && !carry && ndigits == 0)
    {
      // if we rounded off to a 0.0, print it with special handling
      PrintFloat0(e, f, a, uppercaseDigits, formatter, prepend, output, actualsize, end);
    }

    int padtrailing0s = formatter.Precision - ndigits;

    {
      int numwidth = 0;

      // first calculate the width of the produced output, so we can calculate any padding

      numwidth = 3;           // 0x1
      numwidth += ndigits;    // post-decimal digits
      if(ndigits > 0 || (formatter.Flags & AlternateForm) || padtrailing0s > 0)
        numwidth++;    // '.'
      numwidth += padtrailing0s;
      numwidth += 2;    // 'p+' or 'p-'
      if(exponent >= 0xff || exponent <= -0xff)
        numwidth += 3;
      else if(exponent >= 0xf || exponent <= -0xf)
        numwidth += 2;
      else
        numwidth += 1;
      if(prepend)
        numwidth++;    // +, - or ' '

      int padlen = 0;

      if(formatter.Width != FormatterParams::NoWidth && formatter.Width > numwidth)
        padlen = formatter.Width - numwidth;

      // pad with 0s or ' 's and insert the sign character
      if(formatter.Flags & PadZeroes)
      {
        if(a)
          appendstring(output, actualsize, end, uppercaseDigits ? "0X" : "0x");

        if(prepend)
          addchar(output, actualsize, end, prepend);
        addchars(output, actualsize, end, size_t(padlen), '0');
      }
      else if(padlen > 0 && (formatter.Flags & LeftJustify) == 0)
      {
        addchars(output, actualsize, end, size_t(padlen), ' ');
        if(prepend)
          addchar(output, actualsize, end, prepend);

        if(a)
          appendstring(output, actualsize, end, uppercaseDigits ? "0X" : "0x");
      }
      else
      {
        if(prepend)
          addchar(output, actualsize, end, prepend);

        if(a)
          appendstring(output, actualsize, end, uppercaseDigits ? "0X" : "0x");
      }

      if(carry)
        addchar(output, actualsize, end, '2');
      else
        addchar(output, actualsize, end, '1');

      // insert the decimals
      if(ndigits > 0 || (formatter.Flags & AlternateForm) || padtrailing0s > 0)
        addchar(output, actualsize, end, '.');
      for(int i = 0; i < ndigits; i++)
        addchar(output, actualsize, end, digits[i]);

      // add the trailing 0s here
      if(padtrailing0s > 0)
        addchars(output, actualsize, end, size_t(padtrailing0s), '0');

      // print the p+XXX exponential
      addchar(output, actualsize, end, uppercaseDigits ? 'P' : 'p');
      if(exponent >= 0)
        addchar(output, actualsize, end, '+');
      else
        addchar(output, actualsize, end, '-');

      int exponaccum = exponent >= 0 ? exponent : -exponent;

      if(exponaccum >= 1000)
        addchar(output, actualsize, end, '0' + char(exponaccum / 1000));
      exponaccum %= 1000;

      if(exponaccum >= 100)
        addchar(output, actualsize, end, '0' + char(exponaccum / 100));
      exponaccum %= 100;

      if(exponaccum >= 10)
        addchar(output, actualsize, end, '0' + char(exponaccum / 10));
      exponaccum %= 10;

      addchar(output, actualsize, end, '0' + char(exponaccum));

      if(padlen > 0 && (formatter.Flags & LeftJustify))
      {
        addchars(output, actualsize, end, size_t(padlen), ' ');
      }
    }
  }
  else
  {
    // call out to grisu2 to generate digits + exponent
    char digits[18] = {0};

    int K = 0;
    int ndigits = grisu2(mantissa, exponent, digits, K);

    // this is the decimal exponent (ie. 0 if the digits are 1.2345)
    int expon = K + ndigits - 1;

    // number of digits after the decimal
    int decdigits = ndigits - expon - 1;

    // for exponential form, this is always 1 less than the total number of digits
    if(e)
      decdigits = RDCMAX(0, ndigits - 1);

    // see if we need to trim some digits (for %g, the precision is the number of
    // significant figures which is just ndigits at the moment, will be padded with 0s
    // later).
    if(decdigits > formatter.Precision || (g && ndigits > formatter.Precision))
    {
      int removedigs = decdigits - formatter.Precision;

      if(g)
        removedigs = RDCMAX(0, ndigits - formatter.Precision);

      // if we're removing all digits, just check the first to see if it should be
      // rounded up or down
      if(removedigs == ndigits)
      {
        ndigits = 1;
        if(digits[0] < '5')
        {
          digits[0] = '0';
        }
        else
        {
          // round up to "1" on the next exponent
          digits[0] = '1';
          expon++;
        }
      }
      else if(removedigs > ndigits)
      {
        ndigits = 1;
        digits[0] = '0';
        expon = 0;
      }
      else
      {
        // remove the specified number of digits
        ndigits -= removedigs;

        // round up the last digit (continually rolling up if necessary)
        // note this will look 'ahead' into the last removed digits at first
        bool carry = true;
        for(int i = ndigits - 1; i >= 0; i--)
        {
          // should we round up?
          if(digits[i + 1] >= '5')
          {
            digits[i + 1] = 0;

            // unless current digit is a 9, we can just increment it and stop
            if(digits[i] < '9')
            {
              digits[i]++;
              carry = false;
              break;
            }

            // continue (carry to next digit)
          }
          else
          {
            // didn't need to round up, everything's fine.
            carry = false;
            break;
          }

          // trim off a digit (was a 9)
          ndigits--;
          continue;
        }

        // we only get here with carry still true if digits are 9999999
        if(carry)
        {
          // round up to "1" on the next exponent
          ndigits = 1;
          digits[0] = '1';
          expon++;
        }
      }
    }

    // recalculate decimal digits with new ndigits
    decdigits = ndigits - expon - 1;
    if(e)
      decdigits = RDCMAX(0, ndigits - 1);

    // number of trailing 0s we need to pad after decimal point determined by
    // the precision
    int padtrailing0s = formatter.Precision - RDCMAX(0, decdigits);

    if(g)
    {
      // for %g if the exponent is too far out of range, we revert to exponential form
      if(expon >= formatter.Precision || expon < -4)
      {
        e = true;

        // if not alternate form, all trailing 0 digits are removed and there is no padding.
        if((formatter.Flags & AlternateForm) == 0)
        {
          while(ndigits > 1 && digits[ndigits - 1] == '0')
            ndigits--;

          padtrailing0s = 0;
        }
        else
          padtrailing0s = formatter.Precision - RDCMAX(0, ndigits);
      }
      else
      {
        padtrailing0s = formatter.Precision - RDCMAX(0, ndigits);
      }
    }

    // exponential display
    if(e)
    {
      int numwidth = 0;

      // first calculate the width of the produced output, so we can calculate any padding

      numwidth = ndigits;    // digits
      if(ndigits > 1 || (formatter.Flags & AlternateForm) || padtrailing0s > 0)
        numwidth++;    // '.'
      numwidth += padtrailing0s;
      numwidth += 2;    // 'e+' or 'e-'
      if(expon >= 1000 || expon <= -1000)
        numwidth += 4;
      else if(expon >= 100 || expon <= -100)
        numwidth += 3;
      else
        numwidth += 2;
      if(prepend)
        numwidth++;    // +, - or ' '

      int padlen = 0;

      if(formatter.Width != FormatterParams::NoWidth && formatter.Width > numwidth)
        padlen = formatter.Width - numwidth;

      // pad with 0s or ' 's and insert the sign character
      if(formatter.Flags & PadZeroes)
      {
        if(prepend)
          addchar(output, actualsize, end, prepend);
        addchars(output, actualsize, end, size_t(padlen), '0');
      }
      else if(padlen > 0 && (formatter.Flags & LeftJustify) == 0)
      {
        addchars(output, actualsize, end, size_t(padlen), ' ');
        if(prepend)
          addchar(output, actualsize, end, prepend);
      }
      else
      {
        if(prepend)
          addchar(output, actualsize, end, prepend);
      }

      // insert the mantissa as a 1.23456 decimal
      addchar(output, actualsize, end, digits[0]);
      if(ndigits > 1 || (formatter.Flags & AlternateForm) || padtrailing0s > 0)
        addchar(output, actualsize, end, '.');
      for(int i = 1; i < ndigits; i++)
        addchar(output, actualsize, end, digits[i]);

      // add the trailing 0s here
      if(padtrailing0s > 0)
        addchars(output, actualsize, end, size_t(padtrailing0s), '0');

      // print the e-XXX exponential
      addchar(output, actualsize, end, uppercaseDigits ? 'E' : 'e');
      if(expon >= 0)
        addchar(output, actualsize, end, '+');
      else
        addchar(output, actualsize, end, '-');

      int exponaccum = expon >= 0 ? expon : -expon;

      if(exponaccum >= 1000)
        addchar(output, actualsize, end, '0' + char(exponaccum / 1000));
      exponaccum %= 1000;

      if(exponaccum >= 100)
        addchar(output, actualsize, end, '0' + char(exponaccum / 100));
      exponaccum %= 100;
      addchar(output, actualsize, end, '0' + char(exponaccum / 10));
      exponaccum %= 10;
      addchar(output, actualsize, end, '0' + char(exponaccum));

      if(padlen > 0 && (formatter.Flags & LeftJustify))
      {
        addchars(output, actualsize, end, size_t(padlen), ' ');
      }
    }
    else if(digits[0] == '0' && ndigits == 1)
    {
      // if we rounded off to a 0.0, print it with special handling
      PrintFloat0(e, f, a, uppercaseDigits, formatter, prepend, output, actualsize, end);
    }
    else
    {
      // we're printing as a normal decimal, e.g. 12345.6789

      // if %g and not in alternate form, all 0s after the decimal point are stripped
      if(g && (formatter.Flags & AlternateForm) == 0)
        while(ndigits > 1 && ndigits - 1 > expon && digits[ndigits - 1] == '0')
          ndigits--;

      int numwidth = 0;

      // first calculate the width of the produced output, so we can calculate any padding

      // always all digits are printed (after trailing 0s optionally removed above)
      numwidth = ndigits;

      if(prepend)
        numwidth++;    // prefix +, - or ' '

      // if the exponent is exactly the number of digits we have, we have one 0 to pad
      // before the decimal point, and special handling of whether to display the decimal
      // point for %g. (note that exponent 0 is mantissa x 10^0 which is 1.2345
      if(expon == ndigits)
      {
        numwidth++;    // 0 before decimal place

        // if in alternate form for %g we print a . and any trailing 0s necessary to make
        // up the precision (number of significant figures)
        if(g && (formatter.Flags & AlternateForm))
        {
          numwidth++;    // .

          if(padtrailing0s > 1)
            numwidth += (padtrailing0s - 1);
        }
        else if(!g)
        {
          // otherwise we only print the . if alternate form is specified or we need to
          // print trailing 0s
          if(padtrailing0s > 0 || (formatter.Flags & AlternateForm))
            numwidth++;    // .
          if(padtrailing0s > 0)
            numwidth += padtrailing0s;
        }
      }
      // exponent greater than ndigits means we have padding before the decimal place
      // and no values after the decimal place
      else if(expon > ndigits)
      {
        numwidth += (expon + 1 - ndigits);    // 0s between digits and decimal place
        if(!g || (formatter.Flags & AlternateForm))
          numwidth++;    // .

        if(padtrailing0s > 0 && (!g || (formatter.Flags & AlternateForm)))
          numwidth += padtrailing0s;
      }
      else if(expon >= 0)
      {
        // expon < ndigits is true here

        if(expon < ndigits - 1 || !g || (formatter.Flags & AlternateForm))
          numwidth++;    // .

        if(g && (formatter.Flags & AlwaysDecimal))
          numwidth += 2;    // .0

        if(padtrailing0s > 0 && (!g || (formatter.Flags & AlternateForm)))
          numwidth += padtrailing0s;
      }
      else    // if(expon < 0)
      {
        numwidth += 2;               // 0.;
        numwidth += (-1 - expon);    // 0s before digits

        if(!g || (formatter.Flags & AlternateForm))
          numwidth += padtrailing0s;
      }

      int padlen = 0;

      // calculate padding and print it (0s or ' 's) with the sign character
      if(formatter.Width != FormatterParams::NoWidth && formatter.Width > numwidth)
        padlen = formatter.Width - numwidth;

      if(formatter.Flags & PadZeroes)
      {
        if(prepend)
          addchar(output, actualsize, end, prepend);
        addchars(output, actualsize, end, size_t(padlen), '0');
      }
      else if(padlen > 0 && (formatter.Flags & LeftJustify) == 0)
      {
        addchars(output, actualsize, end, size_t(padlen), ' ');
        if(prepend)
          addchar(output, actualsize, end, prepend);
      }
      else
      {
        if(prepend)
          addchar(output, actualsize, end, prepend);
      }

      // if the exponent is greater than 0 we have to handle padding,
      // placing it correctly, whether to show the decimal place or not, etc
      if(expon >= 0)
      {
        // print the digits, adding the . at the right column, as long as it's not
        // after the last column AND we are in %g that's not alternate form (ie.
        // trailing 0s and . are stripped)
        for(int i = 0; i < ndigits; i++)
        {
          addchar(output, actualsize, end, digits[i]);

          if(i == expon)
          {
            if(i < ndigits - 1 || !g || (formatter.Flags & AlternateForm))
              addchar(output, actualsize, end, '.');
          }
        }

        // handle printing trailing 0s here as well as a trailing. if it
        // wasn't printed above, and is needed for the print form.
        if(expon == ndigits)
        {
          addchar(output, actualsize, end, '0');

          if(g && (formatter.Flags & AlternateForm))
          {
            addchar(output, actualsize, end, '.');

            if(padtrailing0s > 1)
              addchars(output, actualsize, end, size_t(padtrailing0s - 1), '0');
          }
          else if(!g)
          {
            if(padtrailing0s > 0 || (formatter.Flags & AlternateForm))
              addchar(output, actualsize, end, '.');
            if(padtrailing0s > 0)
              addchars(output, actualsize, end, size_t(padtrailing0s), '0');
          }
          else if(formatter.Flags & AlwaysDecimal)
          {
            addchar(output, actualsize, end, '.');
            addchar(output, actualsize, end, '0');
          }
        }
        else if(expon > ndigits)
        {
          addchars(output, actualsize, end, size_t(expon + 1 - ndigits), '0');
          if(!g || (formatter.Flags & AlternateForm))
            addchar(output, actualsize, end, '.');

          if(padtrailing0s > 0 && (!g || (formatter.Flags & AlternateForm)))
            addchars(output, actualsize, end, size_t(padtrailing0s), '0');

          if(g && (formatter.Flags & AlwaysDecimal))
          {
            addchar(output, actualsize, end, '.');
            addchar(output, actualsize, end, '0');
          }
        }
        else
        {
          if(padtrailing0s > 0 && (!g || (formatter.Flags & AlternateForm)))
            addchars(output, actualsize, end, size_t(padtrailing0s), '0');

          if(ndigits - 1 <= expon && g && (formatter.Flags & AlwaysDecimal))
          {
            addchar(output, actualsize, end, '.');
            addchar(output, actualsize, end, '0');
          }
        }
      }
      // if exponent is less than 0 it's much easier - just print the number as
      // digits at the right column, then any trailing 0s necessary
      else
      {
        appendstring(output, actualsize, end, "0.");
        addchars(output, actualsize, end, size_t(-1 - expon), '0');

        appendstring(output, actualsize, end, digits, size_t(ndigits));

        if(padtrailing0s > 0 && (!g || (formatter.Flags & AlternateForm)))
          addchars(output, actualsize, end, size_t(padtrailing0s), '0');
      }

      if(padlen > 0 && (formatter.Flags & LeftJustify))
      {
        addchars(output, actualsize, end, size_t(padlen), ' ');
      }
    }
  }
}