9 votes

Comment augmenter d'uns unité la plus petite partie fractionnaire d'un nombre décimal ?

Je veux augmenter la plus petite partie fractionnaire d'un décimal de un de sorte que par exemple

decimal d = 0.01
d++
d == 0.02

ou

decimal d = 0.000012349
d++
d == 0.000012350

Comment puis-je faire cela?

12voto

Joe Points 60749

Le type décimal (.NET 2.0 et ultérieur) conserve des zéros significatifs en fin de nombre qui sont le résultat d'un calcul ou du parsing d'une chaîne. Par exemple, 1,2 * 0,5 = 0,60 (la multiplication de deux nombres précis à une décimale donne un résultat précis à 2 décimales, même lorsque la deuxième décimale est zéro) :

decimal result = 1.2M * 0.5M;
Console.WriteLine(result.ToString()); // affiche 0.60

Le code suivant suppose que vous souhaitez prendre en compte tous les chiffres significatifs de votre valeur décimale, c'est-à-dire :

decimal d = 1.2349M;       // original  1.2349;
d = IncrementLastDigit(d); // le résultat est 1.2350;
d = IncrementLastDigit(d); // le résultat est 1.2351; (pas 1.2360).

Cependant, si vous souhaitez d'abord supprimer les zéros en fin de nombre, vous pouvez le faire, par exemple en utilisant la technique dans cette réponse.

Il n'y a rien de prévu pour le faire automatiquement. Vous devrez le faire vous-même en (a) déterminant combien de chiffres se trouvent après la virgule, puis (b) ajoutant la quantité appropriée.

Pour déterminer combien de chiffres se trouvent après la virgule, vous pouvez soit formater en tant que chaîne, puis les compter, soit plus efficacement, appeler decimal.GetBits(), dont le résultat est un tableau de quatre entiers qui contient le facteur d'échelle dans les bits 16-23 du quatrième entier.

Une fois que vous avez cela, vous pouvez facilement calculer la valeur requise à ajouter à votre valeur décimale.

Voici une implémentation qui utilise GetBits, qui "incrémente" en s'éloignant de zéro pour les nombres négatifs IncrementLastDigit(-1.234M) => -1.235M.

static decimal IncrementLastDigit(decimal value)
{
    int[] bits1 = decimal.GetBits(value);
    int saved = bits1[3];
    bits1[3] = 0;   // Définir l'échelle à 0, supprimer le signe
    int[] bits2 = decimal.GetBits(new decimal(bits1) + 1);
    bits2[3] = saved; // Restaurer l'échelle et le signe d'origine
    return new decimal(bits2);
}

Ou voici une alternative (peut-être légèrement plus élégante) :

static decimal GetScaledOne(decimal value)
{
    int[] bits = decimal.GetBits(value);
    // Générer une valeur +1, mise à l'échelle en utilisant le même facteur d'échelle que la valeur d'entrée
    bits[0] = 1;
    bits[1] = 0;
    bits[2] = 0;
    bits[3] = bits[3] & 0x00FF0000;
    return new decimal(bits);
}

static decimal IncrementLastDigit(decimal value)
{
    return value < 0 ? value - GetScaledOne(value) : value + GetScaledOne(value);
}

2voto

ChaosPandion Points 37025

J'ai trouvé une nouvelle solution qui est différente de celle de Joe, cela devrait entraîner une légère augmentation des performances.

public static decimal IncrementLowestDigit(this decimal value, int amount)
{
    int[] bits = decimal.GetBits(value);
    if (bits[0] < 0 && amount + bits[0] >= 0)
    {
        bits[1]++;
        if (bits[1] == 0)
        {
            bits[2]++;
        }
    }
    bits[0] += amount;
    return new decimal(bits);
}

Test

J'ai testé mes résultats avec les méthodes de Joe.

private static void Test(int l, int m, int h, int e, int times)
{
    decimal a = new decimal(new[] { l, m, h, e });
    decimal b = a.IncrementLowestDigit(times);
    decimal c = IncrementLastDigit(a, times);

    Console.WriteLine(a);
    Console.WriteLine(b);
    Console.WriteLine(c);
    Console.WriteLine();
}

Test(0, 0, 0, 0x00000000, 1);
Test(0, 0, 0, 0x00000000, 2);
Test(0, 0, 0, 0x00010000, 1);
Test(0, 0, 0, 0x00010000, 2);
Test(0, 0, 0, 0x00020000, 1);
Test(0, 0, 0, 0x00020000, 2);

Test(-1, 0, 0, 0x00000000, 1);
Test(-1, 0, 0, 0x00000000, 2);
Test(-1, 0, 0, 0x00010000, 1);
Test(-1, 0, 0, 0x00010000, 2);
Test(-1, 0, 0, 0x00020000, 1);
Test(-1, 0, 0, 0x00020000, 2);

Test(-2, 0, 0, 0x00000000, 1);
Test(-2, 0, 0, 0x00000000, 2);
Test(-2, 0, 0, 0x00010000, 1);
Test(-2, 0, 0, 0x00010000, 2);
Test(-2, 0, 0, 0x00020000, 1);
Test(-2, 0, 0, 0x00020000, 2);
Test(-2, 0, 0, 0x00000000, 3);

Test(0, 1, 0, 0x00000000, 1);
Test(0, 1, 0, 0x00000000, 2);
Test(0, 1, 0, 0x00010000, 1);
Test(0, 1, 0, 0x00010000, 2);
Test(0, 1, 0, 0x00020000, 1);
Test(0, 1, 0, 0x00020000, 2);

Test(-1, 2, 0, 0x00000000, 1);
Test(-1, 2, 0, 0x00000000, 2);
Test(-1, 2, 0, 0x00010000, 1);
Test(-1, 2, 0, 0x00010000, 2);
Test(-1, 2, 0, 0x00020000, 1);
Test(-1, 2, 0, 0x00020000, 2);

Test(-2, 3, 0, 0x00000000, 1);
Test(-2, 3, 0, 0x00000000, 2);
Test(-2, 3, 0, 0x00010000, 1);
Test(-2, 3, 0, 0x00010000, 2);
Test(-2, 3, 0, 0x00020000, 1);
Test(-2, 3, 0, 0x00020000, 2);

Juste pour rire

J'ai effectué un test de performance avec 10 millions d'itérations sur une puce Intel de 3 Ghz :

Le mien : 11.6 ns

Celui de Joe : 32.1 ns

0voto

Paolo Tedesco Points 22442

Que diriez-vous de ceci :

static class DecimalExt {
    public static decimal PlusPlus(this decimal value) {
        decimal test = 1M;
        while (0 != value % test){
            test /= 10;
        }
        return value + test;
    }
}

class Program {

    public static void Main(params string[] args) {
        decimal x = 3.14M;
        x = x.PlusPlus(); // maintenant c'est 3.15
    }
}

J'ai utilisé une méthode d'extension ici; vous ne pouvez pas redéfinir l'opérateur ++ pour le type decimal.

0voto

Marcel Gheorghita Points 945

Cela ferait l'affaire :

decimal d = 0.01M;
int incr = 1;

int pos = d.ToString().IndexOf('.');
int len = d.ToString().Length - pos - 1;

if (pos > 0)
{
   double val = Convert.ToDouble(d);
   val = Math.Round(val * Math.Pow(10, len) + incr) / Math.Pow(10, len);
   d = Convert.ToDecimal(val);
}
else
   d += incr;
return d;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X