Thinko
Thinko

Reputation: 414

C# exponential format: force the first digit to be zero

I have no problem converting a double to such a string: 7.8746137240E-008

I don't know how to force the first digit to always be zero: 0.7874613724E-007

How to achieve that using a custom string format in C#?

Upvotes: 0

Views: 351

Answers (4)

JosephDaSilva
JosephDaSilva

Reputation: 1197

You can do this by formatting with standard exponential notation followed by some post-processing:

public static string FormatNumberExpZero(double value, IFormatProvider format = null) {
    if (!double.IsFinite(value))    // Infinity and NaN
        return value.ToString(format);

    // Format the number to a temporary buffer.
    // "E10" means exponential notation with 10 decimal places.
    Span<char> buffer = stackalloc char[24];
    value.TryFormat(buffer, out int charCount, "E10", format);

    // Don't touch any negative sign.
    Span<char> bufferNoSign = (buffer[0] == '-') ? buffer.Slice(1) : buffer;

    // Move everything after '.' one character forward to make space for the additional zero.
    bufferNoSign.Slice(2, charCount - 2).CopyTo(bufferNoSign.Slice(3));
    charCount++;

    // Change 'X.' to '0.X'
    bufferNoSign[2] = bufferNoSign[0];
    bufferNoSign[1] = '.';
    bufferNoSign[0] = '0';

    // Read the exponent from the buffer.
    Span<char> expChars = buffer.Slice(charCount - 4, 4);
    int exponent = (expChars[1] - '0') * 100 + (expChars[2] - '0') * 10 + expChars[3] - '0';

    if (expChars[0] == '-')
        exponent = -exponent;

    // Add 1 to the exponent to compensate.
    exponent++;

    // Write the new exponent back.
    expChars[0] = (exponent < 0) ? '-' : '+';

    int expAbs = (exponent < 0) ? -exponent : exponent;
    int expDigit1 = expAbs / 100;
    int expDigit2 = (expAbs - expDigit1 * 100) / 10;
    int expDigit3 = expAbs - expDigit1 * 100 - expDigit2 * 10;
    Console.WriteLine((expDigit1, expDigit2, expDigit3));

    expChars[1] = (char)(expDigit1 + '0');
    expChars[2] = (char)(expDigit2 + '0');
    expChars[3] = (char)(expDigit3 + '0');

    // Create the string.
    return new string(buffer.Slice(0, charCount));
}

This solution is better than the one by @MarkSouls because it does not suffer from floating-point inaccuracy and/or overflow to infinity of doing value * 10. This requires .NET Standard 2.1 and so doesn't work with .NET Framework, though it can be modified to work with it (at the cost of allocating an additional string and char array).

Upvotes: 1

Thinko
Thinko

Reputation: 414

I found a simple solution:

value.ToString("\\0.0000000000E+000;-\\0.0000000000E+000")

Upvotes: 1

Klamsi
Klamsi

Reputation: 906

Maybe do it yourself ;)

double foo = 7.8746137240E-008;
var numOfDigits = foo == 0 ? 0 : (int)Math.Ceiling(Math.Log10(Math.Abs(foo)));
string formatString = string.Format("{0:0.000000}E{1:+000;-000;+000}", foo / Math.Pow(10, numOfDigits), numOfDigits);

Upvotes: 1

MarkSouls
MarkSouls

Reputation: 999

I know no fancy way of achieving what you want but you can do it by writing your own function.

public static class Extender
{
    public static string MyToString(this double value)
    {
        string s = (value * 10).ToString("E");
        s = s.Replace(".", "");
        return "0." + s;
    }
}

It's just modifying exponential count and moving . front then adding 0.

public static void Main(string[] args)
{
    Console.WriteLine(1d.MyToString());
    Console.WriteLine(3.14159.MyToString());
    Console.WriteLine(0.0033.MyToString());
    Console.WriteLine(999414128.0.MyToString());
}

/* Output
0.1000000E+001
0.3141590E+001
0.3300000E-002
0.9994141E+009
*/

Not super cool code but it works, though I didn't check edge cases.

I wonder if there's more formal way to do it.

Upvotes: 0

Related Questions