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\$
  • \$\begingroup\$ Following Hans' advice: Math.Truncate(100 * value) / 100; where 100 can be substituted by 1 * 10^places \$\endgroup\$ – Jeroen Vannevel May 28 '14 at 21:50
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\$
  • 2
    \$\begingroup\$ This will fail on negative numbers. \$\endgroup\$ – nightcoder May 17 '16 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, privacy policy and cookie policy

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