views:

8504

answers:

7

If I have a double (234.004223) etc. I would like to round this to x significant digits after the decimal places in C#

So far I can only find ways to round to x decimal places but this simply removes the precision if there are any 0s in the number.

e.g. 0.086 to 1 decimal place becomes 0.1 but I would like it to stay 0.08.

Thanks

+1  A: 

If I understand you correctly you don't want to round but to truncate:

Math.Abs(100 * x) / 100.0;

or

Math.Truncate(x);
0xA3
A: 

This question is similiar to the one you're asking:

http://stackoverflow.com/questions/158172/formatting-numbers-with-significant-figures-in-c#158942

Thus you could do the following:

double Input2 = 234.004223;
string Result2 = Math.Floor(Input2) + Convert.ToDouble(String.Format("{0:G1}", Input2 - Math.Floor(Input2))).ToString("R6");

Rounded to 1 significant digit.

Bravax
+4  A: 

It sounds to me like you don't want to round to x decimal places at all - you want to round to x significant digits. So in your example, you want to round 0.086 to one significant digit, not one decimal place.

Now, using a double and rounding to a number of significant digits is problematic to start with, due to the way doubles are stored. For instance, you could round 0.12 to something close to 0.1, but 0.1 isn't exactly representable as a double. Are you sure you shouldn't actually be using a decimal? Alternatively, is this actually for display purposes? If it's for display purposes, I suspect you should actually convert the double directly to a string with the relevant number of significant digits.

If you can answer those points, I can try to come up with some appropriate code. Awful as it sounds, converting to a number of significant digits as a string by converting the number to a "full" string and then finding the first significant digit (and then taking appropriate rounding action after that) may well be the best way to go.

Jon Skeet
It is for display purposes, I haven't considered a Decimal at all to be honest.How would I go about converting to string with the relevant number of significant digits as you say? I have been unable to find an example in the Double.ToString() method spec.
Rocco
+9  A: 

The framework doesn't have a built-in function to round (or truncate, as in your example) to a number of significant digits. One way you can do this, though, is to scale your number so that your first significant digit is right after the decimal point, round (or truncate), then scale back. The following code should do the trick:

static double RoundToSignificantDigits(this double d, int digits){
    double scale = Math.Pow(10, Math.Floor(Math.Log10(d)) + 1);
    return scale * Math.Round(d / scale, digits);
}

If, as in your example, you really want to truncate, then you want:

static double TruncateToSignificantDigits(this double d, int digits){
    double scale = Math.Pow(10, Math.Floor(Math.Log10(d)) + 1 - digits);
    return scale * Math.Truncate(d / scale);
}
P Daddy
Math.round(...) doesn't take two parameters
leftbrainlogic
@leftbrainlogic: Yes, it really does: http://msdn.microsoft.com/en-us/library/75ks3aby.aspx
P Daddy
+1  A: 

Let inputNumber be Input that needs to be converted with significantDigitsRequired after decimal point, then significantDigitsResult is the answer to the following pseudo code.

integerPortion = Math.truncate(inputNumber)

decimalPortion = myNumber-IntegerPortion

if( decimalPortion <> 0 ) {

significantDigitsStartFrom = Math.Ceil(-log10(decimalPortion))

scaleRequiredForTruncation= Math.Pow(10,significantDigitsStartFrom-1+significantDigitsRequired)

siginficantDigitsResult = integerPortion + ( Math.Truncate (decimalPortion*scaleRequiredForTruncation))/scaleRequiredForTruncation

} else {

siginficantDigitsResult = integerPortion

}

lakshmanaraj
A: 

I've been using pDaddy's sigfig function for a few months and found a bug in it. You cannot take the Log of a negative number, so if d is negative the results is NaN.

The following corrects the bug:

    public static double SetSigFigs(double d, int digits)
    {   
        double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);

        return scale * Math.Round(d / scale, digits);
    }
Eric
For some reason this code won't convert 50.846113537656557 to 6 sigfigs accurately, any ideas?
Andrew Hancox
What results do you get?
Eric
Wow, just noticed this. It's neat that you posted this a year to the day after I posted mine. You're right that mine did not take negative numbers into account. But you've reimplemented `Math.Abs()`, here. You could simplify it to `Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1) and skip the `d >= 0` check.
P Daddy
good call, edited post to reflect
Eric
A: 

See my answer on this other thread

Ben