Усечение Два десятичных знака без округления

Допустим, что у меня есть значение 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));
}

Ответ 1

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

Остерегайтесь того, что такие фракции не могут быть точно представлены в плавающей точке.

Ответ 2

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

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)

Ответ 3

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

    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

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

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

результат: 0.54

Ответ 5

Универсальный и быстрый метод (без 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

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

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

Решение Tim Lloyd защищено от переполнения, но оно не слишком быстро.

Следующее решение примерно в 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));
}

Ответ 7

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

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

Ответ 8

Может ли ((long)(3.4679 * 100)) / 100.0 предоставить то, что вы хотите?

Ответ 9

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

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

Это старый вопрос, но многие anwsers плохо работают или переполнены для больших чисел. Я думаю, что ответ д. Нестерова самый лучший: надежный, простой и быстрый. Я просто хочу добавить свои два цента. Я поиграл с десятичными числами, а также проверил исходный код. Из public Decimal (int lo, int mid, int hi, bool isNegative, byte scale) документации конструктора.

Двоичное представление десятичного числа состоит из 1-битного знак, 96-битное целое число и коэффициент масштабирования, используемый для деления целое число и укажите, какая его часть является десятичной дробью. Коэффициент масштабирования - это неявное число 10, возведенное в степень от 0 до 28.

Зная это, мой первый подход состоял в том, чтобы создать еще один decimal, масштаб которого соответствует десятичным знакам, которые я хотел отбросить, затем обрезать его и, наконец, создать десятичное число с желаемым масштабом.

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

Этот метод не быстрее, чем Д. Нестеров, и он более сложный, поэтому я немного поиграл. Я предполагаю, что необходимость создания вспомогательного decimal и двойного извлечения битов делает его медленнее. Со второй попытки я манипулировал компонентами, возвращаемыми методом Decimal.GetBits(Decimal d). Идея состоит в том, чтобы разделить компоненты в 10 раз по мере необходимости и уменьшить масштаб. Код основан (в значительной степени) на методе Decimal.InternalRoundFromZero(ref Decimal d, int decimalCount).

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

Я не проводил строгие тесты производительности, но на MacOS Sierra 10.12.6, процессоре Intel Core i3 с тактовой частотой 3,06 ГГц и нацеленном на .NetCore 2.1 этот метод выглядит намного быстрее, чем Д. Нестеров (я не буду давать цифры, так как, как я уже говорил, мои тесты не являются строгими). Это зависит от того, кто реализует это, чтобы оценить, окупается ли прирост производительности за сложность добавленного кода.

Ответ 11

Если вы не слишком беспокоитесь о производительности, и конечный результат может быть строкой, следующий подход будет устойчивым к проблемам с плавающей точностью:

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

Ответ 12

Вот моя реализация функции TRUNC

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

Ответ 13

как насчет этого?

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

Ответ 14

Помимо вышеупомянутых решений, мы можем достичь другого.

    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

Ответ 15

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

У меня было десятичное значение 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"

Ответ 16

я использую эту функцию для усечения значения после десятичной в строковой переменной

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

Ответ 17

Вот что я сделал:

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

вычитание двух введенных чисел без округления до десятичной дроби. потому что другие решения не работают для меня. не знаю, будет ли это работать для других, я просто хочу поделиться этим :) Надеюсь, что это работает для тех, кто находит решение проблемы, аналогичной моей. Благодаря

PS: я новичок, поэтому не стесняйтесь указывать на это.: D это хорошо, если вы на самом деле имеете дело с деньгами, ведь центы не так ли? у него только 2 знака после запятой и округление это нет нет.

Ответ 18

На самом деле вы хотите 3.46 от 3.4679. Это только представление символов. Так что нет ничего общего с математической функцией. Функция Math не предназначена для выполнения этой работы. Просто используйте следующий код.

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

Вышеприведенный код может быть изменен для любых чисел. Поставьте следующие код в событии с нажатием кнопки

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