How to truncate a decimal without rounding

How to truncate a decimal without rounding

Introduction

Lately, I have had this odd requirement to truncate decimal places without rounding them. Microsoft provides a Math.Truncate() method, which seems to do the trick, but doesn't take any input parameters to configure the places to cut off.

These were 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 works, but it is quite a clunky approach and 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 if value * multiplier becomes bigger then decimal.Max resulting in an overflow.

So a safer approach would be to handle the integral and fractional parts separately

public static decimal TruncateDigits(this decimal value, int places)
{
    var integral = Math.Truncate(value);
    var fraction = value - integral;

    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));
}
Show Comments