Reputation: 4950
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
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
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.
Upvotes: 2
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