2009-12-04 6 views
3

Je travaille sur un programme Java/Groovy. J'ai une variable double qui contient un nombre qui a été tapé par un utilisateur. Ce que je veux vraiment savoir, c'est combien de chiffres l'utilisateur a tapé à la droite de la décimale. Quelque chose comme:Y at-il un moyen d'obtenir le nombre de places après la virgule décimale dans un double java?

double num = 3.14 
num.getPlaces() == 2 

Bien sûr, vous ne pouvez pas faire cela avec un double puisque c'est en utilisant des points flottante IEEE et il est tout une approximation.

En supposant que je ne peux pas obtenir à la chaîne l'utilisateur a tapé, mais seulement avoir accès au double dans lequel la valeur a été stockée, est-ce que je peux frotter ce double si un BigDecimal ou quelque chose réel "nombre de décimales? (Quand le double est affiché à l'écran, il est correct, donc je suppose qu'il y a moyen de bien deviner?)

+0

Note de bas de page: comme il s'agit d'une base de code Java/Groovy mixte, toute solution utilisant la sorcellerie groovy sera plus que bienvenue. –

+0

Voulez-vous savoir combien de chiffres l'utilisateur a entré après la virgule, ou combien de chiffres seront imprimés après la virgule? Ce sont deux problèmes totalement différents. – erickson

+0

Que l'utilisateur est entré. Bon point. –

Répondre

14

Non, vous ne pouvez pas ... parce qu'il y a beaucoup de différentes chaînes l'utilisateur aurait pu taper ce qui serait tous analysés à la même valeur.

Le nombre "réel" est presque certain d'avoir plus de décimales que l'utilisateur a tapé. Par exemple, 3,14 est stocké exactement comme 3.140000000000000124344978758017532527446746826171875. Je ne pense pas que vous voulez afficher que à l'utilisateur.

Pour rendre le problème plus clair, vous ne pouvez pas dire de la double valeur si l'utilisateur en fait entrée:

3.14 
3.140 
3.140000 
3.14000000000000012434 

Ces quatre chaînes vous donneront tous la même valeur - qui devrait le rendre évident que vous ne peut pas peut revenir à ce que l'utilisateur a tapé dans.

Si à tous possible, changer le code d'analyse syntaxique et utiliser tout au long BigDecimal.

+1

Nitpick: Si vous utilisez 3.14 dans votre code, vous devriez utiliser 'Math.PI' à la place. – Powerlord

+9

@ R.Bemrose: Croyez-vous que le chiffre 3.14 ne se lève jamais sans que cela signifie réellement pi? –

+3

lol @ utilise plutôt pi, probablement pas la réponse qu'il cherche ... –

3

Vous avez raison, il y a quelque chose d'étrange qui se passe avec les doubles, ce qui est imprimé n'est pas le même que le contenu de la variable.

par exemple:

groovy:000> "123.0001".toBigDecimal() 
===> 123.0001 
groovy:000> "123.0001".toDouble() 
===> 123.0001 
groovy:000> new BigDecimal("123.0001".toDouble()) 
===> 123.000100000000003319655661471188068389892578125 

Notez que le dommage est fait dans la conversion de la chaîne à un double, pas quand la double passe dans le BigDecimal. Nourrir le double à BigDecimal fournit juste un moyen facile de voir ce qui est réellement dans le double, car toString vous ment. Comme Jon Skeet le souligne, la précision n'est pas une option ici. Cependant, en supposant que la valeur imprimée sur l'écran est le résultat de l'appel toString sur le double, vous devriez être en mesure d'obtenir un BIGDECIMAL qui est plus mauvais que la version toString du double, comme celui-ci:

groovy:000> d = "123.0001".toDouble() 
===> 123.0001 
groovy:000> d.toString() 
===> 123.0001 
groovy:000> new BigDecimal(d.toString()) 
===> 123.0001 

Donc, vous n'avez pas besoin d'impliquer le BigDecimal, vraiment, vous pouvez juste faire quelque chose comme

groovy:000> d = 123.0001 
===> 123.0001 
groovy:000> s = d.toString() 
===> 123.0001 
groovy:000> s.substring(s.indexOf('.')).length() - 1 
===> 4 

Désolé pour invalider votre commentaire en éditant.

BTW voici quelque chose près de la réponse de Steve, traduit à groovy.(J'ai sorti le test pour le point décimal non trouvé parce que si vous exécutez ceci sur une machine avec des locales foiré ainsi il n'utilise pas une période pour la virgule décimale je préférerais qu'il fasse exploser que retourner 0)

def getPlaces(d) { 
    s = d.toString() 
    s.substring(s.indexOf(".")).length() - 1 
} 
+0

Croyez-moi, je ne le ferais pas si j'avais l'option. Mais, vous savez, la base de code existante et tout ça. Je ne travaille même pas sur le module qui recueille les données, donc je reçois juste une double main. :) –

+0

Man, au moins le mien n'est pas des numéros fiscaux, donc l'erreur d'arrondi occasionnelle est okay. –

+0

"Notez que le dommage est fait dans la conversion de la chaîne en un double ..." est un tour de phrase intéressant. Pour le lecteur occasionnel, le 'dommage' est que le double n'est qu'une approximation. Une grande majorité des nombres entre min double et max double ne sont pas représentables. Il fait de son mieux. :) Double.toString() tente de réapprendre ce que nous voulons dire, car il est plus probable que nous voulions dire 3.14 au lieu de 3.1400000000000001243 ... et ainsi de suite. – PSpeed

0

Si vous êtes bloqué avec un double, convertissez en une chaîne et comptez le nombre de caractères après la virgule. Je pense qu'il y a de la magie impliquée qui affiche des chiffres comme 1.99999999998 comme "2"

0

donc s'il s'agit d'une valeur entrée par l'utilisateur. Acceptez la valeur en tant que chaîne. Ensuite, vous pouvez utiliser les fonctions de chaîne pour trouver la position du "." puis soustrayez ce nombre de la longueur de la chaîne pour obtenir le nombre que vous cherchez. Bien sûr, vous aurez envie de couper(), et vérifiez qu'il s'agit en fait d'un nombre qui a été entré ..

+1

la question mentionne spécifiquement ne pas avoir accès à la chaîne, seule la double valeur. –

1

Si vous devez absolument prendre un double, et cela ne vous dérange pas d'avoir une petite méthode utilitaire à exécuter à travers, vous pourriez écrire quelque chose comme ça. Vous avez dit que vous n'avez pas accès à la valeur String de ce que l'utilisateur a saisi, mais existe-t-il une raison pour laquelle vous ne pouvez pas le convertir en chaîne et faire quelque chose comme ça?

static int getPlaces(double num) { 
    String numString = String.valueOf(num); 

    return numString.indexOf(".0")==numString.length()-2?0:numString.length()-numString.indexOf(".")-1; 
} 

alors, au lieu de votre exemple

num.getPlaces() == 2 

Vous pouvez faire

getplaces(num) == 2 

J'espère que cela aide ..

mise à jour, compte tenu de votre commentaire That the user entered. Good point.

Si l'utilisateur a entré ce qui ressemble à un entier (disons 5) sans point décimal, et que vous le recevez en double, alors ce que vous allez obtenir est quelque chose d'autre que ce que l'utilisateur a entré - pour/il aura entré 5, mais vous aurez reçu 5.0. Vous n'auriez aucun moyen de dire si l'utilisateur a effectivement entré 5 ou 5.0.

+0

BTW, désolé que je n'ai pas une manière groovy d'écrire cette méthode. Je ne connais que java .. – steve

2

Oui, vous pouvez. Au moins, si cela ne vous dérange pas, vous obtenez parfois un mauvais résultat. Nous devons supposer que l'utilisateur est paresseux et n'a pas entré plus de ~ 12 chiffres décimaux significatifs.

font ensuite les éléments suivants:

  • Créer une BigDecimal avec la double donnée dans le constructeur. Cela convertira le double en une représentation décimale exacte.

  • Obtenez uniquement la fraction.

  • Obtenez BigDecimal.toString() et trouvez au moins trois chiffres '0' ou '9' consécutifs.

  • Couper la fraction après ces trois chiffres. Si les chiffres sont neuf, ajoutez un "1" à la fin . Après cela, supprimez tous les zéros finaux.

  • Compter les fractions restantes

    chiffres

Attention: Cela peut être un peu lent.

Si vous voulez seulement le nombre de bits significatifs dans le double, vous pouvez les obtenir beaucoup plus rapidement et plus facilement, mais la conversion décimale-> binaire utilise malheureusement presque tous les bits.

0

Convertir en BigDecimal. BigDecimal.scale() = nombre de places

Questions connexes