How to efficiently ensure a decimal value has at least N decimal places

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.

Jon Skeet
people
quotationmark

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.)

people

See more on this question at Stackoverflow