Truncate Two decimal places without rounding

2019-01-01 00:56发布

问题:

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

回答1:

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

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



回答2:

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)


回答3:

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


回答4:

Use the modulus operator:

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

result: 0.54



回答5:

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


回答6:

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


回答7:

would this work for you?

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


回答8:

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



回答9:

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


回答10:

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


回答11:

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


回答12:

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


回答13:

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


回答14:

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\"


回答15:

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


回答16:

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)


标签: c# math rounding