By user147215


2010-06-29 18:32:05 8 Comments

Lets say I have a value of 3.4679 and want 3.46, how can I truncate to two decimal places that without rounding up?

I have tried the following but all three give me 3.47:

void Main()
{
    Console.Write(Math.Round(3.4679, 2,MidpointRounding.ToEven));
    Console.Write(Math.Round(3.4679, 2,MidpointRounding.AwayFromZero));
    Console.Write(Math.Round(3.4679, 2));
}

This returns 3.46, but just seems dirty some how:

void Main()
{
    Console.Write(Math.Round(3.46799999999 -.005 , 2));
}

17 comments

@Muscicapa Striata 2019-02-11 21:13:24

This is an old question, but many anwsers don't perform well or overflow for big numbers. I think D. Nesterov answer is the best one: robust, simple and fast. I just want to add my two cents. I played around with decimals and also checked out the source code. From the public Decimal (int lo, int mid, int hi, bool isNegative, byte scale) constructor documentation.

The binary representation of a Decimal number consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the integer number and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10 raised to an exponent ranging from 0 to 28.

Knowing this, my first approach was to create another decimal whose scale corresponds to the decimals that I wanted to discard, then truncate it and finally create a decimal with the desired scale.

private const int ScaleMask = 0x00FF0000;
    public static Decimal Truncate(decimal target, byte decimalPlaces)
    {
        var bits = Decimal.GetBits(target);
        var scale = (byte)((bits[3] & (ScaleMask)) >> 16);

        if (scale <= decimalPlaces)
            return target;

        var temporalDecimal = new Decimal(bits[0], bits[1], bits[2], target < 0, (byte)(scale - decimalPlaces));
        temporalDecimal = Math.Truncate(temporalDecimal);

        bits = Decimal.GetBits(temporalDecimal);
        return new Decimal(bits[0], bits[1], bits[2], target < 0, decimalPlaces);
    }

This method is not faster than D. Nesterov's and it is more complex, so I played around a little bit more. My guess is that having to create an auxiliar decimal and retrieving the bits twice is making it slower. On my second attempt, I manipulated the components returned by Decimal.GetBits(Decimal d) method myself. The idea is to divide the components by 10 as many times as needed and reduce the scale. The code is based (heavily) on the Decimal.InternalRoundFromZero(ref Decimal d, int decimalCount) method.

private const Int32 MaxInt32Scale = 9;
private const int ScaleMask = 0x00FF0000;
    private const int SignMask = unchecked((int)0x80000000);
    // Fast access for 10^n where n is 0-9        
    private static UInt32[] Powers10 = new UInt32[] {
        1,
        10,
        100,
        1000,
        10000,
        100000,
        1000000,
        10000000,
        100000000,
        1000000000
    };

    public static Decimal Truncate(decimal target, byte decimalPlaces)
    {
        var bits = Decimal.GetBits(target);
        int lo = bits[0];
        int mid = bits[1];
        int hi = bits[2];
        int flags = bits[3];

        var scale = (byte)((flags & (ScaleMask)) >> 16);
        int scaleDifference = scale - decimalPlaces;
        if (scaleDifference <= 0)
            return target;

        // Divide the value by 10^scaleDifference
        UInt32 lastDivisor;
        do
        {
            Int32 diffChunk = (scaleDifference > MaxInt32Scale) ? MaxInt32Scale : scaleDifference;
            lastDivisor = Powers10[diffChunk];
            InternalDivRemUInt32(ref lo, ref mid, ref hi, lastDivisor);
            scaleDifference -= diffChunk;
        } while (scaleDifference > 0);


        return new Decimal(lo, mid, hi, (flags & SignMask)!=0, decimalPlaces);
    }
    private static UInt32 InternalDivRemUInt32(ref int lo, ref int mid, ref int hi, UInt32 divisor)
    {
        UInt32 remainder = 0;
        UInt64 n;
        if (hi != 0)
        {
            n = ((UInt32)hi);
            hi = (Int32)((UInt32)(n / divisor));
            remainder = (UInt32)(n % divisor);
        }
        if (mid != 0 || remainder != 0)
        {
            n = ((UInt64)remainder << 32) | (UInt32)mid;
            mid = (Int32)((UInt32)(n / divisor));
            remainder = (UInt32)(n % divisor);
        }
        if (lo != 0 || remainder != 0)
        {
            n = ((UInt64)remainder << 32) | (UInt32)lo;
            lo = (Int32)((UInt32)(n / divisor));
            remainder = (UInt32)(n % divisor);
        }
        return remainder;
    }

I haven't performed rigorous performance tests, but on a MacOS Sierra 10.12.6, 3,06 GHz Intel Core i3 processor and targeting .NetCore 2.1 this method seems to be much faster than D. Nesterov's (I won't give numbers since, as I have mentioned, my tests are not rigorous). It is up to whoever implements this to evaluate whether or not the performance gains pay off for the added code complexity.

@Arun kumar 2018-08-08 12:03:17

i am using this function to truncate value after decimal in a string variable

public static string TruncateFunction(string value)
    {
        if (string.IsNullOrEmpty(value)) return "";
        else
        {
            string[] split = value.Split('.');
            if (split.Length > 0)
            {
                string predecimal = split[0];
                string postdecimal = split[1];
                postdecimal = postdecimal.Length > 6 ? postdecimal.Substring(0, 6) : postdecimal;
                return predecimal + "." + postdecimal;

            }
            else return value;
        }
    }

@Nic3500 2018-08-08 12:15:35

While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value.

@kevinwaite 2017-05-26 04:26:58

Under some conditions this may suffice.

I had a decimal value of SubCent = 0.0099999999999999999999999999M that tends to format to |SubCent:0.010000| via string.Format("{0:N6}", SubCent ); and many other formatting choices.

My requirement was not to round the SubCent value, but not log every digit either.

The following met my requirement:

string.Format("SubCent:{0}|", 
    SubCent.ToString("N10", CultureInfo.InvariantCulture).Substring(0, 9));

Which returns the string : |SubCent:0.0099999|

To accommodate the value having an integer part the following is a start.

tmpValFmt = 567890.0099999933999229999999M.ToString("0.0000000000000000000000000000");
decPt = tmpValFmt.LastIndexOf(".");
if (decPt < 0) decPt = 0;
valFmt4 = string.Format("{0}", tmpValFmt.Substring(0, decPt + 9));

Which returns the string :

valFmt4 = "567890.00999999"

@D. Nesterov 2017-04-26 16:37:07

Universal and fast method (without Math.Pow() / multiplication) for System.Decimal:

decimal Truncate(decimal d, byte decimals)
{
    decimal r = Math.Round(d, decimals);

    if (d > 0 && r > d)
    {
        return r - new decimal(1, 0, 0, false, decimals);
    }
    else if (d < 0 && r < d)
    {
        return r + new decimal(1, 0, 0, false, decimals);
    }

    return r;
}

@RichardOD 2018-03-22 11:45:26

I ran this through all of the tests mentioned in the other answers and it works perfectly. Surprised it doesn't have more upvotes. It is worth noting that decimals can only be between 0 and 28 (Probably OK for most people).

@Branko Dimitrijevic 2018-07-16 07:36:33

I second that. This is the best answer. +1

@Corgalore 2012-04-09 20:46:20

It would be more useful to have a full function for real-world usage of truncating a decimal in C#. This could be converted to a Decimal extension method pretty easy if you wanted:

public decimal TruncateDecimal(decimal value, int precision)
{
    decimal step = (decimal)Math.Pow(10, precision);
    decimal tmp = Math.Truncate(step * value);
    return tmp / step;
}

If you need VB.NET try this:

Function TruncateDecimal(value As Decimal, precision As Integer) As Decimal
    Dim stepper As Decimal = Math.Pow(10, precision)
    Dim tmp As Decimal = Math.Truncate(stepper * value)
    Return tmp / stepper
End Function

Then use it like so:

decimal result = TruncateDecimal(0.275, 2);

or

Dim result As Decimal = TruncateDecimal(0.275, 2)

@nightcoder 2016-05-16 23:58:15

This will overflow on large numbers.

@Sarel Esterhuizen 2017-04-16 07:36:24

To add to night coder, the fact that you are using Int32 as intermediary in your function will cause overflows. You should use Int64 if you really must cast it to an Integer. The question would be why you would want to incur that extra overhead anyway since Truncate returns Decimal integrals anyway. Just do something like: decimal step = (decimal)Math.Pow(10, precision); return Math.Truncate(step * value) / step;

@Corgalore 2017-04-24 16:22:05

I dropped the cast to Integer. I left them separate lines for better readability and understanding of how the function works.

@Hameed Syed 2017-03-09 13:41:37

Apart from the above solutions,there is another way we can achieve .

    decimal val=23.5678m,finalValue;

    //take the decimal part    
     int decimalPos = val.ToString().IndexOf('.');
     string decimalPart = val.ToString().Substring(decimalPosition+1,val.ToString().Length);
    //will result.56
   string wholePart=val.ToString().Substring(0,decimalPos-1);
   //concantinate and parse for decimal.
  string truncatedValue=wholePart+decimalPart;//"23.56"
  bool isDecimal=Decimal.tryParse(truncatedValue,out finalValue);//finalValue=23.56

@user2241289 2016-12-03 21:54:54

what about this?

Function TruncateDecimal2(MyValue As Decimal) As Decimal
        Try
            Return Math.Truncate(100 * MyValue) / 100
        Catch ex As Exception
            Return Math.Round(MyValue, 2)
        End Try
End Function

@antony thomas 2016-08-03 18:43:25

Actually you want 3.46 from 3.4679 . This is only representation of characters.So there is nothing to do with math function.Math function is not intended to do this work. Simply use the following code.

Dim str1 As String
str1=""
str1 ="3.4679" 
  Dim substring As String = str1.Substring(0, 3)

    ' Write the results to the screen.
    Console.WriteLine("Substring: {0}", substring)

Or 
    Please use the following code.
Public function result(ByVal x1 As Double) As String 
  Dim i as  Int32
  i=0
  Dim y as String
  y = ""
  For Each ch as Char In x1.ToString
    If i>3 then
     Exit For
    Else
    y + y +ch
    End if
    i=i+1
  Next
  return y
End Function

The above code can be modified for any numbers Put the following code in a button click event

Dim str As String 
str= result(3.4679)
 MsgBox("The number is " & str)

@John Meyer 2016-06-23 20:05:03

Here is an extension method:

public static decimal? TruncateDecimalPlaces(this decimal? value, int places)
    {
        if (value == null)
        {
            return null;
        }

        return Math.Floor((decimal)value * (decimal)Math.Pow(10, places)) / (decimal)Math.Pow(10, places);

    } // end

@ladeangel 2016-06-15 12:36:57

Here is my implementation of TRUNC function

private static object Tranc(List<Expression.Expression> p)
{
    var target = (decimal)p[0].Evaluate();

    // check if formula contains only one argument
    var digits = p.Count > 1
        ? (decimal) p[1].Evaluate()
        : 0;

    return Math.Truncate((double)target * Math.Pow(10, (int)digits)) / Math.Pow(10, (int)digits);
}

@nightcoder 2016-05-17 21:43:41

I will leave the solution for decimal numbers.

Some of the solutions for decimals here are prone to overflow (if we pass a very large decimal number and the method will try to multiply it).

Tim Lloyd's solution is protected from overflow but it's not too fast.

The following solution is about 2 times faster and doesn't have an overflow problem:

public static class DecimalExtensions
{
    public static decimal TruncateEx(this decimal value, int decimalPlaces)
    {
        if (decimalPlaces < 0)
            throw new ArgumentException("decimalPlaces must be greater than or equal to 0.");

        var modifier = Convert.ToDecimal(0.5 / Math.Pow(10, decimalPlaces));
        return Math.Round(value >= 0 ? value - modifier : value + modifier, decimalPlaces);
    }
}

[Test]
public void FastDecimalTruncateTest()
{
    Assert.AreEqual(-1.12m, -1.129m. TruncateEx(2));
    Assert.AreEqual(-1.12m, -1.120m. TruncateEx(2));
    Assert.AreEqual(-1.12m, -1.125m. TruncateEx(2));
    Assert.AreEqual(-1.12m, -1.1255m.TruncateEx(2));
    Assert.AreEqual(-1.12m, -1.1254m.TruncateEx(2));
    Assert.AreEqual(0m,      0.0001m.TruncateEx(3));
    Assert.AreEqual(0m,     -0.0001m.TruncateEx(3));
    Assert.AreEqual(0m,     -0.0000m.TruncateEx(3));
    Assert.AreEqual(0m,      0.0000m.TruncateEx(3));
    Assert.AreEqual(1.1m,    1.12m.  TruncateEx(1));
    Assert.AreEqual(1.1m,    1.15m.  TruncateEx(1));
    Assert.AreEqual(1.1m,    1.19m.  TruncateEx(1));
    Assert.AreEqual(1.1m,    1.111m. TruncateEx(1));
    Assert.AreEqual(1.1m,    1.199m. TruncateEx(1));
    Assert.AreEqual(1.2m,    1.2m.   TruncateEx(1));
    Assert.AreEqual(0.1m,    0.14m.  TruncateEx(1));
    Assert.AreEqual(0,      -0.05m.  TruncateEx(1));
    Assert.AreEqual(0,      -0.049m. TruncateEx(1));
    Assert.AreEqual(0,      -0.051m. TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.14m.  TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.15m.  TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.16m.  TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.19m.  TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.199m. TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.101m. TruncateEx(1));
    Assert.AreEqual(0m,     -0.099m. TruncateEx(1));
    Assert.AreEqual(0m,     -0.001m. TruncateEx(1));
    Assert.AreEqual(1m,      1.99m.  TruncateEx(0));
    Assert.AreEqual(1m,      1.01m.  TruncateEx(0));
    Assert.AreEqual(-1m,    -1.99m.  TruncateEx(0));
    Assert.AreEqual(-1m,    -1.01m.  TruncateEx(0));
}

@Gqqnbig 2016-11-15 23:47:35

I don't like suffixing "Ex" to it. C# supports overloadding, your Truncate method will be groupped together with .net native ones, giving user a seamless experience.

@Jon Senchyna 2016-11-30 20:58:39

Your algorithm results in some incorrect results. The default MidpointRounding mode is Banker's Rounding, which rounds 0.5 to the nearest even value. Assert.AreEqual(1.1m, 1.12m.TruncateEx(1)); fails because of this. If you specify "normal" rounding (AwayFromZero) in the Math.Round call, then Assert.AreEqual(0m, 0m.TruncateEx(1)); fails

@Jon Senchyna 2016-11-30 21:00:54

The only way this solution will work is if you use MidpointRounding.AwayFromZero and specifically code to handle the value 0.

@nightcoder 2016-12-02 02:32:11

"Assert.AreEqual(1.1m, 1.12m.TruncateEx(1)); fails" - What do you mean "fails"? This code has been tested on .NET 4, 4.5.2 & 4.6.1. I use it at work and in my personal projects, and the unit test in my answer is executed every day. And it passes. Am I missing something? Could you provide a screenshot or exception details if my test fails on your machine?

@Ho Ho Ho 2017-02-28 18:23:05

Jon is correct: 0m.TruncateEx(0) results in -1 unless 0 is explicitly handled. Likewise -11m.TruncateEx(0) results in -10 unless MidpointRounding.AwayFromZero is used within Math.Round. Seems to work well with those modifications though.

@Ho Ho Ho 2017-02-28 18:45:05

Even with changes for AwayFromZero and explicit handling of 0, -9999999999999999999999999999m.TruncateEx(0) results in -9999999999999999999999999998, so it is still fallible in some cases.

@Leonard Lewis 2014-08-22 16:33:03

Use the modulus operator:

var fourPlaces = 0.5485M;
var twoPlaces = fourPlaces - (fourPlaces % 0.01M);

result: 0.54

@Tim Lloyd 2013-01-31 15:33:59

One issue with the other examples is they multiply the input value before dividing it. There is an edge case here that you can overflow decimal by multiplying first, an edge case, but something I have come across. It's safer to deal with the fractional part separately as follows:

    public static decimal TruncateDecimal(this decimal value, int decimalPlaces)
    {
        decimal integralValue = Math.Truncate(value);

        decimal fraction = value - integralValue;

        decimal factor = (decimal)Math.Pow(10, decimalPlaces);

        decimal truncatedFraction = Math.Truncate(fraction * factor) / factor;

        decimal result = integralValue + truncatedFraction;

        return result;
    }

@TheKingDave 2013-08-19 14:34:35

I know this is old but I noticed and issue with this. The factor you have here is an int and so if you are truncating to a large number of decimal places (say 25) it will cause the end result to have precision error. I fixed it by changing the factor type to decimal.

@Ignacio Soler Garcia 2013-09-23 09:09:30

@TheKingDave: probably it's irrelevant but as factor cannot have decimals should be better to model it as long right?

@TheKingDave 2013-09-23 09:29:33

@SoMoS For me Decimal worked better because it gave me the highest storage values for factor. It still has a limitation but it is big enough for my application. Long on the other hand wasn't able to store large enough numbers for my application. For example if you do a Truncate(25) with long then there will be some inaccuracy.

@Tim Lloyd 2013-09-23 14:58:50

Updated to allow truncation to a greater number of places as per @TheKingDave suggestion, thanks.

@David Airapetyan 2013-08-05 19:45:15

If you don't worry too much about performance and your end result can be a string, the following approach will be resilient to floating precision issues:

string Truncate(double value, int precision)
{
    if (precision < 0)
    {
        throw new ArgumentOutOfRangeException("Precision cannot be less than zero");
    }

    string result = value.ToString();

    int dot = result.IndexOf('.');
    if (dot < 0)
    {
        return result;
    }

    int newLength = dot + precision + 1;

    if (newLength == dot + 1)
    {
        newLength--;
    }

    if (newLength > result.Length)
    {
        newLength = result.Length;
    }

    return result.Substring(0, newLength);
}

@David Airapetyan 2013-08-05 19:48:47

Actually, hardcoding '.' is not a good idea, better use System.Globalization.CultureInfo.CurrentCulture.NumberFormat‌​.NumberDecimalSepara‌​tor[0]

@Frank 2010-06-29 18:39:22

Would ((long)(3.4679 * 100)) / 100.0 give what you want?

@John Boker 2010-06-29 18:37:17

would this work for you?

Console.Write(((int)(3.4679999999*100))/100.0);

@Hans Passant 2010-06-29 18:36:31

value = Math.Truncate(100 * value) / 100;

Beware that fractions like these cannot be accurately represented in floating point.

@driis 2010-06-29 18:39:09

Use decimal for your values and this answer will work. It is unlikely to always work in any floating point representation.

@Steve314 2010-06-29 18:53:12

That makes me wonder whether it should be possible to specify rounding direction in floating point literals. Hmmmm.

@Hans Passant 2016-05-17 12:02:32

There has to be some way to tell the programmer that calculating with the assumption that a number can store more than 308 digits is grossly inappropriate. Double can store only 15. Overflow is very much a feature here, it overflowed rather badly.

@nightcoder 2016-05-17 21:36:17

I'm sorry, I thought "value" is decimal.

Related Questions

Sponsored Content

63 Answered Questions

[SOLVED] Round to at most 2 decimal places (only if necessary)

13 Answered Questions

[SOLVED] JavaScript math, round to two decimal places

  • 2013-04-02 11:18:30
  • Smudger
  • 463152 View
  • 344 Score
  • 13 Answer
  • Tags:   javascript math

22 Answered Questions

[SOLVED] Limiting floats to two decimal points

15 Answered Questions

[SOLVED] C# Double - ToString() formatting with two decimal places but no rounding

  • 2010-03-16 11:40:21
  • kjv
  • 333893 View
  • 130 Score
  • 15 Answer
  • Tags:   c# string double

15 Answered Questions

[SOLVED] How do I round a decimal value to 2 decimal places (for output on a page)

13 Answered Questions

[SOLVED] Round a double to 2 decimal places

  • 2010-05-11 06:36:50
  • Rajesh
  • 1023627 View
  • 406 Score
  • 13 Answer
  • Tags:   java double rounding

29 Answered Questions

[SOLVED] How to round a number to n decimal places in Java

12 Answered Questions

[SOLVED] Formatting Decimal places in R

  • 2010-08-09 19:57:41
  • Brandon Bertelsen
  • 397941 View
  • 214 Score
  • 12 Answer
  • Tags:   r formatting rounding

2 Answered Questions

[SOLVED] How to truncate a BigDecimal without rounding

3 Answered Questions

[SOLVED] Round floating value to .1 (tens place)

Sponsored Content