Obtenir le nombre de chiffres avant le point décimal
J'ai une variable de decimal
type et je veux vérifier le nombre de chiffres avant la virgule point.
Que dois-je faire? Par exemple, 467.45
doit retourner 3
.
- Apparemment, personne n'est inquiet de savoir si la culture actuelle de l'
ToString()
comprendra les séparateurs de milliers. - Je ne veux pas être un grand PIA, mais "Doit retourner 3!" pourrait être source de confusion du point de vue mathématique. Il ressemble un peu comme "40-32/2 est 4!"
- ne
0.1
avoir un ou zéro chiffres avant la virgule? - zeore chiffres je pense
- édité ma réponse à contenir moins d'encombrement et un code optimisé. Il est devenu à peu près comme le Gris de la réponse, mais un différentiel en boucle et en boucle la version.
- Il n'est pas possible de spécifier que vous souhaitez utiliser l'invariant de la culture lors du tournage d'un nombre en une chaîne de caractères?
Vous devez vous connecter pour publier un commentaire.
Solution sans avoir à les convertir à
string
(ce qui peut être dangereux dans le cas de cultures exotiques):Notez que cette solution est valable pour toutes les valeurs décimalesMise à JOUR
En fait, cette solution ne fonctionne pas avec certaines valeurs, par exemple:
999999999999998
,999999999999999
,9999999999999939
...Évidemment, 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
string
solutions 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 si.Grâce à des commentateurs, honte sur moi, leçon pour vous.
Math.Abs
pour gérer les valeurs négatives ainsi. Et peut-être vérifier sid
est0
)Math
méthodes, c'est toujours au moins 2 fois plus rapide que le "tronquer" solution", cependant, je préfère Stephan réponse la brièveté et la clartéreturn Math.Abs(d) < 10 ? 1 : ...
GetNumberOfDigits(999999999999999)
doit retourner15
mais renvoie16
avec votre code (au moins sur mon ordinateur, doubles ne sont pas homogènes selon les ordinateurs).double
a une précision d'environ. De 15 à 17 chiffres décimaux significatifs, quelque chose comme9999999999999999993728621.07m
devront être arrondis (en haut ou en bas, selon l'endroit où la plus proche multiple de la puissance de deux, mais dans certains (~50%) des cas, il sera sûrement question).0 < n < 1
. La condition est en fait inutile. La partie après le:
est tout ce dont vous avez besoin.Math.Floor()
est également redondante. Zéro peut être traitée comme un cas particulier. Valeurs > 999999999999997 ne fonctionne pas correctement.int
ne sont pas les mêmes pour les valeurs négatives (Log10(x) + 1
est négatif pour les très petitesx
).0..1
valeurs - vous avez raison, répondre à besoin d'être mis à jour suite à l'OP de nouveaux commentaires. Et encore quelques-uns des grands paramètres conduisent à de mauvais résultats comme il l'a déclaré dans une mise à jour du textestatic int GetNumberOfDigits(decimal d) { decimal abs = Math.Abs(d); return abs < 1 ? 0 : (int)(Math.Log10(decimal.ToDouble(abs)) + 1); }
depuis GetNumberOfDigits(0,5 m) doit retourner 1 au lieu de 0.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 tous opérations sont effectuées sur des décimales et pas de casting est impliqué.
De sortie est
29
. Pour exécuter cet exemple, visiter ce lien.Côté remarque: certains tests montrent des résultats surprenants (10k pistes):
while ((d = decimal.Floor(d /10m)) != 0m)
: 25mswhile ((d = d /10m) > 1m)
: 32msÉgalement à 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.
0.456m
doit être considéré comme égal à zéro chiffres, tandis que votre solution renvoie 1 pour elle.ToString()
decimal.Floor
dans la première ligne de votre fonction, de prendre la valeur absolue (qui est, vous pouvez simplement écriredecimal d = dec < 0 ? decimal.Negate(dec) : dec;
, sans emballage de la RHS dans undecimal.Floor
). Aussi, je ne pense pas que vous avez besoin de la répétition de l'Étage, soit: essayez de faire de votre boucle justewhile (d >= 10) { cnt++; d = d / 10; }
. Comment ces comparer à l'encontre de vos repères?Je voudrais essayer ceci:
Si vous voulez être sûr de ne pas avoir d'étranges résultats pour les différentes cultures et négatif de décimales, vous feriez mieux d'utiliser ceci:
ToString(CultureInfo.InvariantCulture)
, ou vous pouvez obtenir certains assez fou de résultats.decimal
en premier lieu.Je préfère le suivant à la place de casting pour
int
pour s'assurer que vous pouvez également gérer les grands nombres (par exemple,decimal.MaxValue
):Comme une méthode;
Note: 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 uneOverflowException
.Dans ce cas, à l'aide de
long
au lieu deint
peut une meilleure approche. Cependant même unlong
(System.Int64
) n'est pas assez grande pour contenir tous les possiblesdecimal
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;
long
n'est pas assez grand pour représenter tous lesdecimal
s.int
,long
oudouble
fonctionne. Certains de la chaîne en fonction des réponses de travail, mais ils doivent utiliserInvariantCulture
, et la division en fonction des réponses de travail ainsi.long
? Ouais, il ne peut pas tenir toutdecimal
valeurs. Je suis au courant de cela, mais est-il aussi une autre raison? Qu'en pensez-vous?Voici un récursives exemple (surtout pour le fun).
0
a 0 chiffres? C'est intéressant..54
et1.54
autant que nombre de chiffres va. Autrement, vous feriez traiter de la même manière..1
ayant pas de chiffres avant la virgule, mais peut-être qu'il a compris0
. Oh bien..1
et0.1
depuis qu'ils sont égaux. Tout dépend de ce que ce serait en fait être utilisé pour. Si c'était pour enseigner aux enfants à propos de décimales, vous pouvez avoir une réponse différente que si vous avez écrit un programme pour un satellite, qui tient compte de la relativité restreinte.Math.Floor(Math.Log10((double)n) + 1);
est le chemin à parcourir.La conversion à
int
est MAUVAIS parce quedecimal
peut être plus grande queint
:Math.Floor(n).ToString().Count();
est mauvais, car il peut inclure des milliers séparateurs.Log
fonctions dans leMath
classe de gérer uniquementdouble
s.GetNumberOfDigits(999999999999999)
doit retourner à 15, mais renvoie 16 avec votre code (au moins sur mon ordinateur, doubles ne sont pas homogènes selon les ordinateurs).Si vous avez un penchant pour les petits nombres, vous pouvez utiliser quelque chose de plus simple comme ça.
Elle est divisée en deux méthodes, de sorte que la première méthode est plus petit et peut être insérée.
Rendement est à peu près la même que la solution avec les Log10, mais sans les erreurs d'arrondi. La Méthode utilisant Log10, est toujours le plus rapide (un peu) plus spécialement pour les nombres > 1 million de dollars.
Ce code est généré en utilisant le suivant modèle T4:
Cela va faire si vous ne voulez vraiment pas à utiliser la méthode Log (qui OMI est le meilleur moyen). C'est à propos de la façon la plus claire que je peux penser de cette opération à l'aide ToString():
Ou alternativement, si vous ne voulez pas compter
0.123M
comme ayant un chiffre:Vous pourriez utiliser la fonction ToString avec un format personnalisé.
La
#
spécifier que vous souhaitez seulement les caractères avant le.
La
System.Globalization.CultureInfo.InvariantCulture
vous assurer que vous n'aurez pas de formatage de la Région de l'Option.Donc, j'ai couru en avant, et résolu avec ce code:
SqlDecimal
fait partie de laSystem.Data.SqlTypes
espace de noms. "Précision" est le nombre total de chiffres significatifs, tout en "Échelle" est le nombre de chiffres après le point décimal.Maintenant, je connais une objection à aller dans cette voie, c'est que
SqlDecimal
partie de la SQL Server-code spécifique. C'est un point valide, mais je tiens également à souligner que c'est une partie de l' .NET framework lui-même, et ce, depuis au moins la version 1.1, il me semble qu'il serait encore applicable quel que soit le code, il est en train de faire.J'ai regardé sous le capot avec un décompilateur (JetBrains' dotPeek dans cet exemple), pour voir si peut-être le code pour le calcul de la précision et de l'échelle pourrait être extraite facilement et simplement, sans tirant dans
SqlDecimal
. Le code pour calculer l'échelle est très simple, mais la méthode pour calculer la précision est non-trivial, donc si c'était moi, je venais de passer parSqlDecimal
.Les mathématiques de la façon de le faire (et probablement la plus rapide) est d'obtenir une logarytm de la base 10 d'une valeur absolue de ce nombre et autour d'elle
jusqu'.
Log10
ne fonctionne que surdouble
, pasdecimal
. Il y a des valeursdecimal
peut représenter, mais où cette fonction donne un mauvais résultat.TLDR toutes les autres réponses. J'ai écrit ceci en PHP, et le calcul serait le même. (Si je savais que C# j'aurais écrit dans cette langue.)
Je n'en doute pas, il ya une meilleure façon, mais je voulais la jeter dans mon $.02
EDIT:
Je php-isés le code je l'ai mentionné dans mes commentaires, mais fait disparaître avec l'int de conversion.
Cette réponse est à peu près la levée de Calculer Système.La Précision décimale et l'Échelle mais avec une petite modification pour s'adapter à la question posée.
Aussi, si vous voulez le faire sans l'aide de la SqlDecimal classe découvrez Jon Skeet réponse à la même question.
Utiliser le modulo, je ne suis pas un programmeur C#, mais je suis sûr que cette solution de travail:
Ce serait la solution Java
float
nombre n'est pasdecimal
.Decimal
type dans .NET est sensiblement plus grande queint
, voir d'autres semblables réponses et commentairesJAVA
solution.decimal
balise en question. En outre, autant que je sache, où sont en quelque sorte desDecimal
type dansjava
trop.Si vous traiter de zéros ou de l'absence de zéros en tant que numéro 1, c'est OK. Si vous voulez zéro retour zéro ou le manque de zéro retour zéro, alors il ya quelques cas de travail qui ne devrait pas être trop dur à ajouter. Aussi, si la valeur Absolue de gérer les nombres négatifs. Ajouté que les cas de test ainsi.
Ici est un peu la solution finale, si vous trouvez un cas de test qui ne fonctionne pas, faites le moi savoir. Il doit retourner 3,0,0,0,3 pour le cas de test fourni.
Voici ma version optimisée du code inspiré par le Gris de la réponse:
Si vous ne voulez pas les Maths.Abs d'être appelée à l'intérieur de cette fonction, assurez-vous d'utiliser
en dehors de la fonction du paramètre avant d'appeler GetNumOfDigits.
J'ai décidé de supprimer les autres codes pour réduire l'encombrement dans ma réponse, même si ils m'ont aidé à arriver à ce point...
Si il y a des améliorations nécessaires, alors laissez-moi savoir et je vais le mettre à jour :).
Afin d'obtenir une précision et culturellement agnostique de la réponse, je ne la suivante:
System.Numerics.BigInteger
, dont le constructeur accepte une virgule et ne pas semblent produire toutes les erreurs d'arrondi.BigInteger.Abs()
pour enlever tout signe.BigInteger.ToString()
avec le "#" format pour supprimer toutes les séparateurs qui pourrait se produire.Code
Vous pouvez faire cela en arrondissant ce nombre, puis obtenir la longueur de ce nouveau numéro. Vous pourriez faire comme ceci:
Les autres solutions de perdre chiffres si le nombre est trop grand.
Algorithme:
|decimal|
à la Chaîne."."
existent dans de la virgule, les couper avant, sinon, envisager l'ensemble du numéro.Exemple:
Code:
.
mais,
?.
,,
n'est pas un point décimal, mais peut apparaître lorsque vous essayezdecimal.ToString("C");
qui est la monnaie de la représentation.
, il suffit donc deToString()
pouvez retourner"3,14"
au lieu de"3.14"
Je n'ai pas testé, mais j'aimerais garder simple et à faire:
Ce ne gère pas les nombres négatifs. Si vous vous souciez de ceux d'en envisager de prendre la valeur absolue. En outre, si vous voulez 0 avant la virgule à pas comptés, alors vous pouvez simplement utiliser une instruction if pour vérifier.
InvariantCulture
à convertir en chaîne (de préférence), ou à la requête de la culture actuelle pour le séparateur décimal.simple :