wstring NumberToString()

in src/CalcManager/Ratpack/conv.cpp [1076:1264]


wstring NumberToString(_Inout_ PNUMBER& pnum, NumberFormat format, uint32_t radix, int32_t precision)
{
    stripzeroesnum(pnum, precision + 2);
    int32_t length = pnum->cdigit;
    int32_t exponent = pnum->exp + length; // Actual number of digits to the left of decimal

    NumberFormat oldFormat = format;
    if (exponent > precision && format == NumberFormat::Float)
    {
        // Force scientific mode to prevent user from assuming 33rd digit is exact.
        format = NumberFormat::Scientific;
    }

    // Make length small enough to fit in pret.
    if (length > precision)
    {
        length = precision;
    }

    // If there is a chance a round has to occur, round.
    // - if number is zero no rounding
    // - if number of digits is less than the maximum output no rounding
    PNUMBER round = nullptr;
    if (!zernum(pnum) && (pnum->cdigit >= precision || (length - exponent > precision && exponent >= -MAX_ZEROS_AFTER_DECIMAL)))
    {
        // Otherwise round.
        round = i32tonum(radix, radix);
        divnum(&round, num_two, radix, precision);

        // Make round number exponent one below the LSD for the number.
        if (exponent > 0 || format == NumberFormat::Float)
        {
            round->exp = pnum->exp + pnum->cdigit - round->cdigit - precision;
        }
        else
        {
            round->exp = pnum->exp + pnum->cdigit - round->cdigit - precision - exponent;
            length = precision + exponent;
        }

        round->sign = pnum->sign;
    }

    if (format == NumberFormat::Float)
    {
        // Figure out if the exponent will fill more space than the non-exponent field.
        if ((length - exponent > precision) || (exponent > precision + 3))
        {
            if (exponent >= -MAX_ZEROS_AFTER_DECIMAL)
            {
                round->exp -= exponent;
                length = precision + exponent;
            }
            else
            {
                // Case where too many zeros are to the right or left of the
                // decimal pt. And we are forced to switch to scientific form.
                format = NumberFormat::Scientific;
            }
        }
        else if (length + abs(exponent) < precision && round)
        {
            // Minimum loss of precision occurs with listing leading zeros
            // if we need to make room for zeros sacrifice some digits.
            round->exp -= exponent;
        }
    }

    if (round != nullptr)
    {
        addnum(&pnum, round, radix);
        int32_t offset = (pnum->cdigit + pnum->exp) - (round->cdigit + round->exp);
        destroynum(round);
        if (stripzeroesnum(pnum, offset))
        {
            // WARNING: nesting/recursion, too much has been changed, need to
            // re-figure format.
            return NumberToString(pnum, oldFormat, radix, precision);
        }
    }
    else
    {
        stripzeroesnum(pnum, precision);
    }

    // Set up all the post rounding stuff.
    bool useSciForm = false;
    int32_t eout = exponent - 1; // Displayed exponent.
    MANTTYPE* pmant = pnum->mant + pnum->cdigit - 1;
    // Case where too many digits are to the left of the decimal or
    // NumberFormat::Scientific or NumberFormat::Engineering was specified.
    if ((format == NumberFormat::Scientific) || (format == NumberFormat::Engineering))
    {
        useSciForm = true;
        if (eout != 0)
        {
            if (format == NumberFormat::Engineering)
            {
                exponent = (eout % 3);
                eout -= exponent;
                exponent++;

                // Fix the case where 0.02e-3 should really be 2.e-6 etc.
                if (exponent < 0)
                {
                    exponent += 3;
                    eout -= 3;
                }
            }
            else
            {
                exponent = 1;
            }
        }
    }
    else
    {
        eout = 0;
    }

    // Begin building the result string
    wstring result;

    // Make sure negative zeros aren't allowed.
    if ((pnum->sign == -1) && (length > 0))
    {
        result = L'-';
    }

    if (exponent <= 0 && !useSciForm)
    {
        result += L'0';
        result += g_decimalSeparator;
        // Used up a digit unaccounted for.
    }

    while (exponent < 0)
    {
        result += L'0';
        exponent++;
    }

    while (length > 0)
    {
        exponent--;
        result += DIGITS[*pmant--];
        length--;

        // Be more regular in using a decimal point.
        if (exponent == 0)
        {
            result += g_decimalSeparator;
        }
    }

    while (exponent > 0)
    {
        result += L'0';
        exponent--;
        // Be more regular in using a decimal point.
        if (exponent == 0)
        {
            result += g_decimalSeparator;
        }
    }

    if (useSciForm)
    {
        result += (radix == 10 ? L'e' : L'^');
        result += (eout < 0 ? L'-' : L'+');
        eout = abs(eout);
        wstring expString{};
        do
        {
            expString += DIGITS[eout % radix];
            eout /= radix;
        } while (eout > 0);

        result.insert(result.end(), expString.crbegin(), expString.crend());
    }

    // Remove trailing decimal
    if (!result.empty() && result.back() == g_decimalSeparator)
    {
        result.pop_back();
    }

    return result;
}