Обрезать два десятичных знака без округления - PullRequest
90 голосов
/ 29 июня 2010

Допустим, у меня есть значение 3,4679 и я хочу 3,46, как я могу усечь до двух десятичных знаков без округления?

Я пробовал следующее, но все три дают мне 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));
}

Это возвращает 3,46, но выглядит грязно как:

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

Ответы [ 17 ]

120 голосов
/ 29 июня 2010
value = Math.Truncate(100 * value) / 100;

Помните, что подобные дроби не могут быть точно представлены в плавающей запятой.

49 голосов
/ 10 апреля 2012

Было бы более полезно иметь полноценную функцию для реального использования усечения десятичной дроби в C #.Это может быть легко преобразовано в метод расширения Decimal, если вы хотите:

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

Если вам нужен VB.NET, попробуйте это:

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

Затем используйте его так:

decimal result = TruncateDecimal(0.275, 2);

или

Dim result As Decimal = TruncateDecimal(0.275, 2)
22 голосов
/ 31 января 2013

Одна проблема с другими примерами заключается в том, что они умножают входное значение до его деления. Здесь есть крайний случай, который вы можете переполнить десятичной дробью, умножив сначала, крайний случай, но кое-что, с чем я столкнулся. С дробной частью безопаснее разбираться отдельно:

    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;
    }
20 голосов
/ 22 августа 2014

Используйте оператор модуля:

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

результат: 0,54

15 голосов
/ 26 апреля 2017

Универсальный и быстрый метод (без Math.Pow() / умножение) для 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 голосов
/ 18 мая 2016

Я оставлю решение для десятичных чисел.

Некоторые решения для десятичных чисел здесь склонны к переполнению (если мы пропустим очень большое десятичное число и метод попытается его умножить).

Решение Тима Ллойда защищено от переполнения, но оно не слишком быстрое.

Следующее решение примерно в 2 раза быстрее и не имеет проблемы переполнения:

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));
}
2 голосов
/ 29 июня 2010

будет ли это работать для вас?

Console.Write(((int)(3.4679999999*100))/100.0);
2 голосов
/ 29 июня 2010

Даст ли ((long)(3.4679 * 100)) / 100.0 то, что вы хотите?

1 голос
/ 23 июня 2016

Вот метод расширения:

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
0 голосов
/ 26 мая 2017

При некоторых условиях этого может быть достаточно.

У меня было десятичное значение SubCent = 0.0099999999999999999999999999M , которое имеет тенденцию к формату | SubCent: 0.010000 | через string.Format("{0:N6}", SubCent ); и многие другие варианты форматирования.

Моим требованием было не округлять значение SubCent, а также не регистрировать каждую цифру.

Следующее соответствует моему требованию:

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

Возвращает строку: | SubCent: 0.0099999 |

Для размещения значения, содержащего целую часть, следующее является началом.

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

Возвращаетстрока:

valFmt4 = "567890.00999999"
...