9
\$\begingroup\$

In one finance application I'm working on, the requirements call that I truncate a decimal value at certain number of places and not round.

Example: 11.685 truncated to 2 decimal places should be 11.68

I wrote this extension method, but was wondering if someone knew of an easier way.

public static class DecimalExtentsions
{
    public static decimal TruncateDecimalPlaces(this decimal val, int places)
    {
        if (places <= 0)
            return Math.Truncate(val);

        var dv = Math.Pow(10, places);
        decimal part = (val % 1) * Convert.ToDecimal(dv);
        val -= (part % 1) / Convert.ToDecimal(dv);
        return val;
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Following Hans' advice: Math.Truncate(100 * value) / 100; where 100 can be substituted by 1 * 10^places \$\endgroup\$ Commented May 28, 2014 at 21:50

3 Answers 3

7
\$\begingroup\$

One can use Math.Round() to truncate a number by removing 0.5 / 10 ^ places and then rounding

e.g
11.685 to 2 places

Math.Round(11.685 - 0.005, 2)  

The following seems to do the job

public static class NumberExtensions {
    public static decimal TruncateDecimalPlaces(this decimal val, int places) {
        if (places < 0) {
            throw new ArgumentException("places");
        }
        return Math.Round(val - Convert.ToDecimal((0.5 / Math.Pow(10, places))), places);
    }
}



[TestMethod]
public void TruncateToPlaces() {

    Assert.AreEqual(10.665m, 10.6651M.TruncateDecimalPlaces( 3));
    Assert.AreEqual(10.665m, 10.6653M.TruncateDecimalPlaces( 3));
    Assert.AreEqual(10.665m, 10.6655M.TruncateDecimalPlaces( 3));
    Assert.AreEqual(10.665m, 10.6657M.TruncateDecimalPlaces( 3));
    Assert.AreEqual(10.665m, 10.665789M.TruncateDecimalPlaces( 3));
    Assert.AreEqual(10.665m, 10.66599999M.TruncateDecimalPlaces( 3));

    Assert.AreEqual(10m, 10.66599999M.TruncateDecimalPlaces( 0));
    Assert.AreEqual(10.6m, 10.66599999M.TruncateDecimalPlaces( 1));
    Assert.AreEqual(10.66m, 10.66599999M.TruncateDecimalPlaces( 2));
    Assert.AreEqual(10.665m, 10.66599999M.TruncateDecimalPlaces( 3));
    Assert.AreEqual(10.6659m, 10.66599999M.TruncateDecimalPlaces( 4));

}
\$\endgroup\$
1
  • 3
    \$\begingroup\$ This will fail on negative numbers. \$\endgroup\$
    – nightcoder
    Commented May 17, 2016 at 0:14
3
\$\begingroup\$

What does dv mean? Use descriptive variable names. Val and Places Make sense. part is okay, but would be better as dec_part or decimal_part. We're no longer restricted on the bytes we use for variables. You shouldn't act like we are.

\$\endgroup\$
0
\$\begingroup\$

My solution multiplies the value by 10places, truncates, and finally divides by the same power of ten. If the places are negative, the decimals before the decimal point are truncated; i.e., 1234.TruncateDecimalPlaces(-2) ==> 1200.

Since Math.Pow is works on doubles, I'm using my own implementation of the power function. However; if the precision of the Math.Pow gives you good enough results, just use that one.

public static decimal TruncateDecimalPlaces(this decimal val, int places)
{
    var dv = DecimalPow(10m, places);
    return Math.Truncate(dv * val) / dv;
}

private static decimal DecimalPow(decimal b, int exp)
{
    decimal result = 1m;
    if (exp >= 0) {
        while (exp > 0) {
            if (exp % 2 != 0) {
                result *= b;
            }
            exp /= 2;
            b *= b;
        }
    } else {
        do {
            if (exp % 2 != 0) {
                result /= b;
            }
            exp /= 2;
            b *= b;
        } while (exp < 0);
    }
    return result;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.