Truncate Two decimal places without rounding

C#MathRounding

C# Problem Overview


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

C# Solutions


Solution 1 - C#

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

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

Solution 2 - C#

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)

Solution 3 - C#

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;
}

Solution 4 - C#

Use the modulus operator:

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

result: 0.54

Solution 5 - C#

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;
    }

Solution 6 - C#

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

Solution 7 - C#

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.

Solution 8 - C#

In .NET Core 3.0 and later Math.Round and Decimal.Round can truncate digits through the new MidpointRounding.ToZero. For positive numbers, MidpointRounding.ToNegativeInfinity has the same effect while for negative numbers the equivalent is MidpointRounding.ToPositiveInfinity.

These lines:

Console.WriteLine(Math.Round(3.4679, 2,MidpointRounding.ToZero));
Console.WriteLine(Math.Round(3.9999, 2,MidpointRounding.ToZero));
Console.WriteLine(Math.Round(-3.4679, 2,MidpointRounding.ToZero));
Console.WriteLine(Math.Round(-3.9999, 2,MidpointRounding.ToZero));

Produce :

3.46
3.99
-3.46
-3.99

Solution 9 - C#

would this work for you?

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

Solution 10 - C#

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

Solution 11 - C#

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

Solution 12 - C#

my favorite is

var myvalue = 54.301012345;
var valueiwant = myvalue.toString("0.00");
//result => "54.30"

//additional
var valueiwant2 = myvalue.toString("0.##");
//result => "54.3" // without zero

Solution 13 - C#

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

Solution 14 - C#

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

Solution 15 - C#

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

Solution 16 - C#

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

Solution 17 - C#

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"

Solution 18 - C#

This is what i did:

        c1 = a1 - b1;
        d1 = Math.Ceiling(c1 * 100) / 100;

subtracting two inputted numbers without rounding up or down the decimals. because the other solutions does not work for me. don't know if it will work for others, i just want to share this :) Hope it works tho for those who's finding solution to a problem similar to mine. Thanks

PS: i'm a beginner so feel free to point out something on this. :D this is good if you're actually dealing with money, cause of the cents right? it only have 2 decimal places and rounding it is a no no.

Solution 19 - C#

public static decimal TruncateDecimalPlaces(this decimal value, int precision)
    {
        try
        {
            step = (decimal)Math.Pow(10, precision);
            decimal tmp = Math.Truncate(step * value);
            return tmp / step;
        }
        catch (OverflowException)
        {
            step = (decimal)Math.Pow(10, -1 * precision);
            return value - (value % step);
        }
    }

Solution 20 - C#

function to truncate any decimal number without rounding

public static double round(double d, byte p) 
{ 
	return Math.Truncate(d * Math.Pow(10, p)) / Math.Pow(10, p); 
}

Solution 21 - C#

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;
        }
    }

Solution 22 - C#

        public static void ReminderDigints(decimal? number, out decimal? Value,  out decimal? Reminder)
        {
            Reminder = null;
            Value = null;
            if (number.HasValue)
            {
                Value = Math.Floor(number.Value);
                Reminder = (number - Math.Truncate(number.Value));
            }
        }



        decimal? number= 50.55m;             
        ReminderDigints(number, out decimal? Value, out decimal? Reminder);

Solution 23 - C#

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)

Solution 24 - C#

what about

var i = Math.Truncate(number);

var r = i + Math.Truncate((number - i) * 100) / 100;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser147215View Question on Stackoverflow
Solution 1 - C#Hans PassantView Answer on Stackoverflow
Solution 2 - C#CorgaloreView Answer on Stackoverflow
Solution 3 - C#D. NesterovView Answer on Stackoverflow
Solution 4 - C#Leonard LewisView Answer on Stackoverflow
Solution 5 - C#Tim LloydView Answer on Stackoverflow
Solution 6 - C#nightcoderView Answer on Stackoverflow
Solution 7 - C#Monticola ExploratorView Answer on Stackoverflow
Solution 8 - C#Panagiotis KanavosView Answer on Stackoverflow
Solution 9 - C#John BokerView Answer on Stackoverflow
Solution 10 - C#FrankView Answer on Stackoverflow
Solution 11 - C#John MeyerView Answer on Stackoverflow
Solution 12 - C#FeelRightzView Answer on Stackoverflow
Solution 13 - C#David AirapetyanView Answer on Stackoverflow
Solution 14 - C#ladeangelView Answer on Stackoverflow
Solution 15 - C#user2241289View Answer on Stackoverflow
Solution 16 - C#Hameed SyedView Answer on Stackoverflow
Solution 17 - C#kevinwaiteView Answer on Stackoverflow
Solution 18 - C#NoojView Answer on Stackoverflow
Solution 19 - C#Mr.Wang from Next DoorView Answer on Stackoverflow
Solution 20 - C#Manuel AlfaroView Answer on Stackoverflow
Solution 21 - C#Arun kumarView Answer on Stackoverflow
Solution 22 - C#Zoyeb ShaikhView Answer on Stackoverflow
Solution 23 - C#antony thomasView Answer on Stackoverflow
Solution 24 - C#JackyView Answer on Stackoverflow