views:

3925

answers:

5

I have a Double which could have a value from around 0.000001 to 1,000,000,000.000

I wish to format this number as a string but conditionally depending on its size. So if it's very small I want to format it with something like:

String.Format("{0:.000000000}", number);

if it's not that small, say 0.001 then I want to use something like

String.Format("{0:.00000}", number);

and if it's over, say 1,000 then format it as:

String.Format("{0:.0}", number);

Is there a clever way to construct this format string based on the size of the value I'm going to format?

+2  A: 

Use the # character for optional positions in the string:

string.Format("{0:#,###,##0.000}", number);

I don't think you can control the number of decimal places like that as the precision of the double will likely mess things up.

To encapsulate the logic of deciding how many decimal places to output you could look at creating a custom formatter.

Andrew Kennan
+10  A: 

Use Math.Log10 of the absolute value of the double to figure out how many 0's you need either left (if positive) or right (if negative) of the decimal place. Choose the format string based on this value. You'll need handle zero values separately.

string s;
double epislon = 0.0000001; // or however near zero you want to consider as zero
if (Math.Abs(value) < epislon) {
    int digits = Math.Log10( Math.Abs( value ));
    // if (digits >= 0) ++digits; // if you care about the exact number
    if (digits < -5) {
       s = string.Format( "{0:0.000000000}", value );
    }
    else if (digits < 0) {
       s = string.Format( "{0:0.00000})", value );
    }
    else {
       s = string.Format( "{0:#,###,###,##0.000}", value );
    }
}
else {
    s = "0";
}

Or construct it dynamically based on the number of digits.

tvanfosson
+2  A: 

The first two String.Format in your question can be solved by automatically removing trailing zeros:

String.Format("{0:#,##0.########}", number);

And the last one you could solve by calling Math.Round(number,1) for values over 1000 and then use the same String.Format.

Something like:

String.Format("{0:#,##0.########}", number<1000 ? number : Math.Round(number,1));
Jonas Elfström
+1  A: 

If it were me, I'd write a custom wrapper class and put tvanfosson's code into its ToString method. That way you could still work with the double value, but you'd get the right string representation in just about all cases. It'd look something like this:

class FormattedDouble
{ 
    public double Value { get; set; }

    protected overrides void ToString()
    {
        // tvanfosson's code to produce the right string
    }
}

Maybe it might be better to make it a struct, but I doubt it would make a big difference. You could use the class like this:

var myDouble = new FormattedDouble();
myDouble.Value = Math.Pi;
Console.WriteLine(myDouble);
OwenP
+1  A: 

Following up on OwenP's (and by "extension" tvanfosson):

If it's common enough, and you're on C# 3.0, I'd turn it into an extension method on the double:

class MyExtensions 
{
    public static string ToFormmatedString(this double d)
    {
        // Take d and implement tvanfosson's code
    }
}

Now anywhere you have a double you can do:

double d = 1.005343;
string d_formatted = d.ToFormattedString();
tophat02