web-dev-qa-db-fra.com

Tronquer deux décimales sans arrondir

Disons que j'ai une valeur de 3,4679 et que je veux 3,46, comment puis-je tronquer à deux décimales sans arrondir?

J'ai essayé ce qui suit mais les trois me donnent 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));
}

Cela retourne 3.46, mais semble tout simplement sale, comment:

void Main()
{
    Console.Write(Math.Round(3.46799999999 -.005 , 2));
}
85
user147215
value = Math.Truncate(100 * value) / 100;

Attention, de telles fractions ne peuvent être représentées avec précision en virgule flottante.

112
Hans Passant

Il serait plus utile d'avoir une fonction complète pour utiliser dans le monde réel la troncature d'une décimale en C #. Cela pourrait être converti en une méthode d'extension Decimal assez facile si vous vouliez:

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

Si vous avez besoin de VB.NET, essayez ceci:

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

Alors utilisez-le comme suit:

decimal result = TruncateDecimal(0.275, 2);

ou

Dim result As Decimal = TruncateDecimal(0.275, 2)
47
Corgalore

Un problème avec les autres exemples est qu'ils multiplient la valeur d'entrée avant la divisant. Il y a un cas Edge ici que vous pouvez dépasser en décimal en multipliant d'abord, un cas Edge, mais quelque chose que j'ai rencontré. Il est plus sûr de traiter la partie fractionnaire séparément comme suit:

    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;
    }
22
Tim Lloyd

Utilisez l'opérateur de module:

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

résultat: 0.54

17
Leonard Lewis

Méthode universelle et rapide (sans Math.Pow()/multiplication) pour 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;
}
11
D. Nesterov

Je vais laisser la solution pour les nombres décimaux.

Certaines des solutions pour les décimales ici sont susceptibles de déborder (si nous passons un très grand nombre décimal et que la méthode tentera de le multiplier).

La solution de Tim Lloyd est protégée contre les débordements, mais pas trop vite.

La solution suivante est environ 2 fois plus rapide et ne présente pas de problème de débordement:

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

cela fonctionnerait-il pour vous?

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

((long)(3.4679 * 100)) / 100.0 donnerait-il ce que vous voulez?

2
Frank

Voici une méthode d'extension:

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
1
John Meyer

et ça?

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
0
user2241289

Dans certaines conditions, cela peut suffire.

J'ai eu une valeur décimale de SubCent = 0.0099999999999999999999999999M qui tend à formater à | SubCent: 0.010000 | via string.Format("{0:N6}", SubCent ); et de nombreux autres choix de formatage.

Mon exigence n'était pas d'arrondir la valeur SubCent, mais de ne pas enregistrer chaque chiffre non plus.

Les éléments suivants ont satisfait à mes exigences:

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

Ce qui retourne la chaîne: | SubCent: 0.0099999 |

Pour prendre en compte la valeur ayant une partie entière, voici un début.

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

Ce qui retourne la chaîne: 

valFmt4 = "567890.00999999"
0
kevinwaite

C'est ce que j'ai fait:

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

soustraire deux nombres entrés sans arrondir les décimales. parce que les autres solutions ne fonctionnent pas pour moi. Je ne sais pas si cela fonctionnera pour les autres, je veux simplement partager ceci :) J'espère que cela fonctionne pour ceux qui trouvent une solution à un problème similaire au mien. Merci

PS: Je suis débutant, alors n'hésitez pas à signaler quelque chose à ce sujet. : D c'est bien si vous avez réellement affaire à de l'argent, à cause des cents, n'est-ce pas? il n'a que 2 décimales et arrondi c'est non.

0
Nooj

Voici mon implémentation de la fonction 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);
}
0
ladeangel

C'est une vieille question, mais beaucoup de réponses ne fonctionnent pas bien et ne débordent pas pour les gros chiffres. Je pense que D. Nesterov répond est la meilleure: robuste, simple et rapide. Je veux juste ajouter mes deux cents . J'ai joué avec décimales et aussi vérifié le code source . De la public Decimal (int lo, int mid, int hi, bool isNegative, byte scale)documentation du constructeur .

La représentation binaire d'un nombre décimal consiste en un bit signe, un nombre entier de 96 bits et un facteur d’échelle utilisé pour diviser le nombre entier et spécifier quelle partie de celui-ci est une fraction décimale . Le facteur d'échelle est implicitement le nombre 10 élevé à un exposant allant de 0 à 28.

Sachant cela, ma première approche a été de créer une autre variable decimal dont l’échelle correspond aux décimales que je voulais rejeter, puis de la tronquer et de créer finalement un nombre décimal avec l’échelle désirée. 

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

Cette méthode n’est pas plus rapide que celle de D. Nesterov et elle est plus complexe. J’ai donc joué un peu plus. Je suppose que devoir créer une variable auxiliaire decimal et extraire les bits deux fois le ralentit. Lors de ma deuxième tentative, j'ai manipulé les composants renvoyés par la méthode Decimal.GetBits (Decimal d) moi-même. L'idée est de diviser les composants par 10 autant de fois que nécessaire et de réduire l'échelle. Le code est basé (fortement) sur la méthode Decimal.InternalRoundFromZero (réf 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;
    }

Je n’ai pas effectué de tests de performances rigoureux, mais sur un processeur Intel Core i3 à 3,06 GHz pour MacOS Sierra 10.12.6 et ciblant .NetCore 2.1, cette méthode semble être beaucoup plus rapide que celle de D. Nesterov (je ne donnerai pas de chiffres, , comme je l’ai mentionné, mes tests ne sont pas rigoureux). C'est à celui qui l'implémentera de déterminer si les gains de performance rapportent ou non pour la complexité du code ajouté.

0
Muscicapa Striata

j'utilise cette fonction pour tronquer la valeur après le nombre décimal dans une variable de chaîne 

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;
        }
    }
0
Arun kumar

Outre les solutions ci-dessus, il existe un autre moyen que nous pouvons atteindre.

    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
0
Hameed Syed

Si vous ne vous inquiétez pas trop des performances et que votre résultat final peut être une chaîne, l'approche suivante résilient aux problèmes de précision flottante:

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);
}
0
David Airapetyan