Introduction
Lately I had this odd requirement to truncate decimal places without actually rounding them. Microsoft provides a Math.Truncate()
method, which seems to do the trick, but doesn't take any input paramters to configure the places to cut off.
This where the solutions that came to mind, most of them not being practical:
Format to string and back to decimal
var value = 1.23456M;
var shortenedValue = Convert.ToDecimal(value.ToString("0.###"));
Assert.Equals(shortenedValue, 1.234);
Okay, this actually works, but is kinda clunky approach and also lacks flexibility.
Use Math.Truncate()
This works by shifting places to the left via a multiplier.
public static decimal TruncateDigits(this decimal value, int places)
{
if (places <= 0)
{
throw new InvalidOperationException("Places to shift must be greater then zero!");
}
var multiplier = (decimal)Math.Pow(10, places);
return Math.Truncate(value * multiplier) / multiplier;
}
There is one issue with this solution, as an overflow could happen in case value * multiplier
becomes bigger then decimal.Max
resuling in an overflow.
So a safer approach would be to handle the integral and fractional part seperatly
public static decimal TruncateDigits(this decimal value, int places)
{
var integral = Math.Truncate(value);
var fraction = value - integralValue;
var multiplier = (decimal)Math.Pow(10, places);
var truncatedFraction = Math.Truncate(fraction * multiplier) / multiplier;
return integral + truncatedFraction;
}
Use a modulus operation
Even another approach would be to use a modulus operation as follows
public static decimal TruncateDigits(this decimal value, int places)
{
if (places <= 0)
{
throw new InvalidOperationException("Should be bigger then 0");
}
var divisor = (decimal)Math.Pow(10, -1 * places);
return (value - (value % divisor));
}