63 votes

Obtenir le nombre de chiffres avant le point décimal

J'ai une variable de type decimal et je veux vérifier le nombre de chiffres avant la virgule décimale. Que devrais-je faire? Par exemple, 467.45 devrait renvoyer 3 .

82voto

astef Points 1199

Solution sans avoir à les convertir string (ce qui peut être dangereux dans le cas de cultures exotiques):

static int GetNumberOfDigits(decimal d)
{
    decimal abs = Math.Abs(d);

    return abs < 1 ? 0 : (int)(Math.Log10(decimal.ToDouble(abs)) + 1);
}

Notez que cette solution est valable pour toutes les valeurs décimales

Mise à JOUR

En fait, cette solution ne fonctionne pas avec certaines valeurs, par exemple: 999999999999998, 999999999999999, 9999999999999939...

De toute évidence, les opérations mathématiques avec double ne sont pas assez précis pour cette tâche.

Lors de la recherche des valeurs erronées j'ai tendance à utiliser stringsolutions de rechange proposées dans cette rubrique. Comme pour moi, c'est la preuve qu'ils sont plus fiables et faciles à utiliser (mais il faut être conscient des cultures). Boucle de solutions peut être plus rapide.

Merci aux commentateurs, honte sur moi, leçon pour vous.

32voto

Markus Points 7812

Au lieu de la conversion en chaîne de caractères, vous pouvez aussi diviser le nombre par 10 jusqu'à ce qu'il est égal à 0. Intéressant, c'est que les opérations mathématiques sur les décimales sont beaucoup plus lent que la conversion du décimal à une chaîne et le retour de la longueur (voir les repères ci-dessous).
Cette solution ne permet pas d'utiliser les Mathématiques-méthodes qui prennent un double comme entrée; de sorte que toutes les opérations sont effectuées sur des décimales et pas de casting est impliqué.

using System;

public class Test
{
    public static void Main()
    {
        decimal dec = -12345678912345678912345678912.456m;
        int digits = GetDigits(dec);
        Console.WriteLine(digits.ToString());
    }

    static int GetDigits(decimal dec)
    {
        decimal d = decimal.Floor(dec < 0 ? decimal.Negate(dec) : dec);
        // As stated in the comments of the question, 
        // 0.xyz should return 0, therefore a special case
        if (d == 0m)
            return 0;
        int cnt = 1;
        while ((d = decimal.Floor(d / 10m)) != 0m)
            cnt++;
        return cnt;
    }
}

La sortie est - 29. Pour exécuter cet exemple, visitez ce lien.


Remarque: certains tests montrent des résultats surprenants (10k pistes):

  • while ((d = decimal.Floor(d / 10m)) != 0m): 25ms
  • while ((d = d / 10m) > 1m): 32ms
  • ToString avec les Mathématiques-double-opérations: 3ms
  • ToString avec décimale-opérations: 3ms
  • BigInt (voir la réponse de @Heinzi): 2ms

Aussi à l'aide de nombres aléatoires au lieu de toujours la même valeur (pour éviter la mise en cache de la virgule à la conversion de chaîne de caractères) a montré que la chaîne méthodes sont beaucoup plus rapides.

26voto

Kevin Brechbühl Points 3079

Je voudrais essayer ceci:

 Math.Truncate(467.45).ToString().Length
 

Si vous voulez être sûr de ne pas avoir de résultats étranges pour différentes cultures et avec des nombres décimaux négatifs, vous feriez mieux d'utiliser ceci:

 var myDecimal = 467.45m;
Math.Truncate(Math.Abs(myDecimal)).ToString(CultureInfo.InvariantCulture).Length
 

13voto

Stephan Bauer Points 3827

Je préférerais ce qui suit au lieu de lancer un casting sur int afin de vous assurer que vous pouvez également gérer de grands nombres (par exemple decimal.MaxValue ):

 Math.Truncate ( Math.Abs ( decValue ) ).ToString( "####" ).Length
 

7voto

Soner Gönül Points 35739
decimal d = 467.45M;
int i = (int)d;
Console.WriteLine(i.ToString(CultureInfo.InvariantCulture).Length); //3

En tant que méthode;

public static int GetDigitsLength(decimal d)
{
  int i = int(d);
  return i.ToString(CultureInfo.InvariantCulture).Length;
}

Remarque: bien sûr, vous devriez d'abord vérifier votre décimales est plus grand que Int32.MaxValue ou pas. Parce que si c'est, vous obtenez un OverflowException.

Dans ce cas, l'aide d' long au lieu de int pouvez une meilleure approche. Cependant , même un long (System.Int64) n'est pas assez grande pour contenir tous les possibles decimal de la valeur.

Comme Rawling mentionné, votre partie peut tenir le séparateur des milliers et mon code sera brisé dans un tel cas. Parce que de cette manière, il ignore totalement mon numéro contient NumberFormatInfo.NumberGroupSeparator ou pas.

C'est pourquoi obtenir seulement des nombres est une meilleure approche. Comme;

i.ToString().Where(c => Char.IsDigit(c)).ToArray()

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