roanjain
roanjain

Reputation: 1252

Truncating a number to specified decimal places

I need to truncate a number to 2 decimal places, which basically means chopping off the extra digits.

Eg:

2.919     ->      2.91

2.91111   ->     2.91

Why? This is what SQL server is doing when storing a number of a particular precision. Eg, if a column is Decimal(8,2), and you try to insert/update a number of 9.1234, the 3 and 4 will be chopped off.

I need to do exactly the same thing in c# code.

The only possible ways that I can think of doing it are either:

  1. Using the stringformatter to "print" it out only two decimal places, and then converting it to a decimal, eg:

      decimal tooManyDigits = 2.1345
    
    decimal ShorterDigits = Convert.ToDecimal(tooManyDigits.ToString("0.##"));
    
    // ShorterDigits is now 2.13
    

    I'm not happy with this because it involves a to-string and then another string to decimal conversion which seems a bit mad.

  2. Using Math.Truncate (which only accepts an integer), so I can multiply it by 100, truncate it, then divide by 100. eg:

    decimal tooLongDecimal = 2.1235;
    
    tooLongDecimal = Math.Truncate(tooLongDecimal * 100) / 100;
    

    I'm also not happy with this because if tooLongDecimal is 0, I'll get a divide by 0 error.

Surely there's a better + easier way! Any suggestions?

Upvotes: 14

Views: 14981

Answers (5)

MrGadget
MrGadget

Reputation: 1268

The previously offered mathematical solutions are vulnerable to overflow with large numbers and/or a large number of decimal places. Consider instead the following extension method:

public static decimal TruncateDecimal(this decimal d, int decimals)
{
    if (decimals < 0)
        throw new ArgumentOutOfRangeException("decimals", "Value must be in range 0-28."); 
    else if (decimals > 28)
        throw new ArgumentOutOfRangeException("decimals", "Value must be in range 0-28.");
    else if (decimals == 0)
        return Math.Truncate(d);
    else
    {
        decimal integerPart = Math.Truncate(d);
        decimal scalingFactor = d - integerPart;
        decimal multiplier = (decimal) Math.Pow(10, decimals);

        scalingFactor = Math.Truncate(scalingFactor * multiplier) / multiplier;

        return integerPart + scalingFactor;
    }
}

Usage:

decimal value = 18446744073709551615.262626263m;
value = value.TruncateDecimal(6); // Result: 18446744073709551615.262626

Upvotes: 7

Shubham Jatale
Shubham Jatale

Reputation: 1

public static decimal Rounding(decimal val, int precision)
    {
        decimal res = Trancating(val, precision + 1);
        return Math.Round(res, precision, MidpointRounding.AwayFromZero);
    }
    public static decimal Trancating(decimal val,int precision)
    {
        if (val.ToString().Contains("."))
        {
            string valstr = val.ToString();
            string[] valArr = valstr.Split('.');
            if(valArr[1].Length < precision)
            {
                int NoOfZeroNeedToAdd = precision - valArr[1].Length;
                for (int i = 1; i <= NoOfZeroNeedToAdd; i++)
                {
                    valstr = string.Concat(valstr, "0");
                }
            }
            if(valArr[1].Length > precision)
            {
                valstr = valArr[0] +"."+ valArr[1].Substring(0, precision);
            }
            return Convert.ToDecimal(valstr); 
        }
        else
        {
            string valstr=val.ToString();
            for(int i = 0; i <= precision; i++)
            {
                    if (i == 1)
                        valstr = string.Concat(valstr, ".0");
                    if(i>1)
                    valstr = string.Concat(valstr, "0");
            }
            return Convert.ToDecimal(valstr);
        }
        
    }

Upvotes: 0

Sridhar Nathani
Sridhar Nathani

Reputation: 101

I agree with p.s.w.g. I had the similar requirement and here is my experience and a more generalized function for truncating.

http://snathani.blogspot.com/2014/05/truncating-number-to-specificnumber-of.html

public static decimal Truncate(decimal value, int decimals)
{
    decimal factor = (decimal)Math.Pow(10, decimals);
    decimal result = Math.Truncate(factor * value) / factor;
    return result;
}

Upvotes: 3

user2246674
user2246674

Reputation: 7719

Using decimal.ToString('0.##') also imposes rounding:

1.119M.ToString("0.##")  // -> 1.12

(Yeah, likely should be a comment, but it's hard to format well as such.)

Upvotes: 1

p.s.w.g
p.s.w.g

Reputation: 148990

You've answered the question yourself; it seems you just misunderstood what division by zero means. The correct way to do this is to multiply, truncate, then devide, like this:

decimal TruncateTo100ths(decimal d)
{
    return Math.Truncate(d* 100) / 100;
}

TruncateTo100ths(0m);       // 0
TruncateTo100ths(2.919m);   // 2.91
TruncateTo100ths(2.91111m); // 2.91
TruncateTo100ths(2.1345m);  // 2.13

There is no division by zero here, there is only division by 100, which is perfectly safe.

Upvotes: 11

Related Questions