I want to efficiently ensure a decimal value has at least N (=3 in the example below) places, prior to doing arithmetic operations.
Obviouly I could format with "0.000######....#"
then parse, but it's relatively inefficient and I'm looking for a solution that avoids converting to/from a string.
I've tried the following solution:
decimal d = 1.23M;
d = d + 1.000M - 1;
Console.WriteLine("Result = " + d.ToString()); // 1.230
which seems to work for all values <= Decimal.MaxValue - 1
when compiled using Visual Studio 2015 in both Debug and Release builds.
But I have a nagging suspicion that compilers may be allowed to optimize out the (1.000 - 1). Is there anything in the C# specification that guarantees this will always work?
Or is there a better solution, e.g. using Decimal.GetBits
?
UPDATE
Following up Jon Skeet's answer, I had previously tried adding 0.000M
, but this didn't work on dotnetfiddle. So I was surprised to see that Decimal.Add(d, 0.000M)
does work. Here's a dotnetfiddle comparing d + 000M
and decimal.Add(d,0.000M)
: the results are different with dotnetfiddle, but identical when the same code is compiled using Visual Studio 2015:
decimal d = 1.23M;
decimal r1 = decimal.Add(d, 0.000M);
decimal r2 = d + 0.000M;
Console.WriteLine("Result1 = " + r1.ToString()); // 1.230
Console.WriteLine("Result2 = " + r2.ToString()); // 1.23 on dotnetfiddle
So at least some behavior seems to be compiler-dependent, which isn't reassuring.
If you're nervous that the compiler will optimize out the operator (although I doubt that it would ever do so) you could just call the Add
method directly. Note that you don't need to add and then subtract - you can just add 0.000m. So for example:
public static decimal EnsureThreeDecimalPlaces(decimal input) =>
decimal.Add(input, 0.000m);
That appears to work fine - if you're nervous about what the compiler will do with the constant, you could keep the bits in an array, converting it just once:
private static readonly decimal ZeroWithThreeDecimals =
new decimal(new[] { 0, 0, 0, 196608 }); // 0.000m
public static decimal EnsureThreeDecimalPlaces(decimal input) =>
decimal.Add(input, ZeroWithThreeDecimals);
I think that's a bit over the top though - particularly if you have good unit tests in place. (If you test against the compiled code you'll be deploying, there's no way the compiler can get in there afterwards - and I'd be really surprised to see the JIT intervene here.)
See more on this question at Stackoverflow