Derrick Moeller
Derrick Moeller

Reputation: 4950

Generating a decimal with trailing zeros

I need to round a decimal to a specific number of significant figures. I have a library that can be simplified as shown.

decimal a = Math.Round(20.051M * 100M) / 100M; // 20.05
decimal b = Math.Round(20.001M * 100M) / 100M; // 20;

Clearly I lost the trailing zeros when performing the Round, I can get them back by adding .00M Is there a way to do this dynamically, if I were rounding to five significant figures I would want to use .000M, likewise if my decimal was < 10 .000M would be appropriate.

b = b + .00M; // 20.00

For reference here is the actual rounding method.

DecimalExtensions.cs

private static readonly decimal[] Pows = Enumerable.Range(-28, 57).Select(p => (decimal)Math.Pow(10, p)).ToArray();

/// <remarks>https://stackoverflow.com/a/18146056/3194005</remarks>
public static decimal RoundToSignificantFigures(this decimal value, int significantFigures)
{
    if (value == 0)
        return 0;

    int d = Log10Ceiling(Math.Abs(value));
    int power = significantFigures - d;

    decimal magnitude = (decimal)Math.Pow(10, power);

    return Math.Round(value * magnitude) / magnitude;
}

private static int Log10Ceiling(decimal value)
{
    int log10 = Array.BinarySearch(Pows, value);

    return (log10 >= 0 ? log10 : ~log10) - 28;
}

Upvotes: 2

Views: 2337

Answers (3)

to StackOverflow
to StackOverflow

Reputation: 124696

This is not an answer, is too long for a comment, and too specific for a new question, so...

I asked a similar question here, and discovered an anomaly, whereby the dotnetfiddle compiler returns 20 for your example:

decimal b = Math.Round(20.001M * 100M) / 100M; 
b = b + .00M; 

Whereas it (correctly) returns 20.00 for:

decimal b = Math.Round(20.001M * 100M) / 100M; 
b = Decimal.Add(b, .00M);

Both return 20.00 with the Visual Studio compilers I have tried (VS2013, VS2015).

I don't know if this is a compiler bug, but in the absence of an explanation I'd prefer the second version.

Upvotes: 2

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726489

You can make a value with the required number of zeros after the decimal point by computing the value of mag-1, and subtracting it from itself:

var res = Math.Round(value * magnitude) / magnitude;
var invMag = 1/magnitude;
return res + invMag - invMag;

In your example mag=102, so invMag=10-2, or 0.01. When you subtract 0.01M - 0.01M you end up with 0.00M, which you can add to result to force it to have the desired number of trailing zeros without changing the value.

Demo

Upvotes: 2

Kevin Smith
Kevin Smith

Reputation: 14436

The value of 20M is the same as 20.00M, if it's formatting for the user just do

20M.ToString("0.00");

Checkout NumberFormatInfo - https://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo(v=vs.110).aspx

Upvotes: -1

Related Questions