web-dev-qa-db-fra.com

Déplacer les décimales dans un double

Donc, j'ai un double set égal à 1234, je veux déplacer une décimale sur 12,34

Donc, pour ce faire, je multiplie .1 à 1234 deux fois, un peu comme ça

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.println(x);

Cela affichera le résultat "12.340000000000002"

Existe-t-il un moyen, sans simplement le formater à deux décimales, d’avoir le double magasin 12.34 correctement?

94
BlackCow

Si vous utilisez double ou float, vous devez arrondir ou attendre quelques erreurs d'arrondi. Si vous ne pouvez pas faire cela, utilisez BigDecimal.

Le problème que vous avez est que 0.1 n’est pas une représentation exacte, et en effectuant le calcul deux fois, vous composez cette erreur.

Cependant, 100 peut être représenté avec précision, alors essayez:

double x = 1234;
x /= 100;
System.out.println(x);

qui imprime:

12.34

Cela fonctionne parce que Double.toString(d) effectue une petite quantité d'arrondi pour vous, mais ce n'est pas beaucoup. Si vous vous demandez à quoi cela pourrait ressembler sans arrondir:

System.out.println(new BigDecimal(0.1));
System.out.println(new BigDecimal(x));

impressions:

0.100000000000000005551115123125782702118158340454101562
12.339999999999999857891452847979962825775146484375

En résumé, l’arrondi est inévitable pour les réponses sensibles en virgule flottante, que vous le fassiez explicitement ou non.


Remarque: x / 100 Et x * 0.01 Ne sont pas exactement les mêmes en ce qui concerne l'erreur d'arrondi. En effet, l'erreur d'arrondi pour la première expression dépend des valeurs de x, alors que le 0.01 Dans la seconde a une erreur d'arrondi fixe.

for(int i=0;i<200;i++) {
    double d1 = (double) i / 100;
    double d2 = i * 0.01;
    if (d1 != d2)
        System.out.println(d1 + " != "+d2);
}

empreintes

0.35 != 0.35000000000000003
0.41 != 0.41000000000000003
0.47 != 0.47000000000000003
0.57 != 0.5700000000000001
0.69 != 0.6900000000000001
0.7 != 0.7000000000000001
0.82 != 0.8200000000000001
0.83 != 0.8300000000000001
0.94 != 0.9400000000000001
0.95 != 0.9500000000000001
1.13 != 1.1300000000000001
1.14 != 1.1400000000000001
1.15 != 1.1500000000000001
1.38 != 1.3800000000000001
1.39 != 1.3900000000000001
1.4 != 1.4000000000000001
1.63 != 1.6300000000000001
1.64 != 1.6400000000000001
1.65 != 1.6500000000000001
1.66 != 1.6600000000000001
1.88 != 1.8800000000000001
1.89 != 1.8900000000000001
1.9 != 1.9000000000000001
1.91 != 1.9100000000000001
185
Peter Lawrey

Non - si vous voulez stocker les valeurs décimales avec précision, utilisez BigDecimal. double simplement ne peut pas représenter un nombre tel que 0,1 exactement, pas plus que vous ne pouvez écrire la valeur d'un tiers avec un nombre fini de chiffres décimaux.

52
Jon Skeet

si c'est juste le formatage, essayez printf

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.printf("%.2f",x);

sortie

12.34
46
Augusto

Dans les logiciels financiers, il est courant d'utiliser des entiers pour quelques centimes. À l'école, on nous a appris à utiliser le virgule fixe au lieu de la virgule flottante, mais c'est généralement deux fois plus puissant. Stocker des centimes dans des entiers pourrait également être appelé "point fixe".

int i=1234;
printf("%d.%02d\r\n",i/100,i%100);

En classe, on nous a demandé en général quels nombres peuvent être représentés exactement dans une base.

Pour base=p1^n1*p2^n2... vous pouvez représenter n’importe quel N où N = n * p1 ^ m1 * p2 ^ m2.

Laisser base=14=2^1*7^1... vous pouvez représenter 1/7 1/14 1/28 1/49 mais pas 1/3

Je connais les logiciels financiers. J'ai converti les rapports financiers de Ticketmaster de VAX asm en Pascal. Ils avaient leur propre formatln () avec des codes pour quelques centimes. La raison de la conversion était 32 bits entiers ne suffisaient plus. +/- 2 milliards de pennies, c'est 20 millions de dollars et ça a débordé pour la Coupe du Monde ou les Jeux Olympiques, j'ai oublié.

J'ai juré de garder le secret. Tant pis. Dans les universités, si c'est bien, vous publiez; dans l'industrie, vous gardez le secret.

26

vous pouvez essayer la représentation d'un nombre entier

int i =1234;
int q = i /100;
int r = i % 100;

System.out.printf("%d.%02d",q, r);
12
Angel Koh

Cela est dû à la manière dont les ordinateurs stockent les nombres à virgule flottante. Ils ne le font pas exactement. En tant que programmeur, vous devriez lire ce guide en virgule flottante pour vous familiariser avec les essais et les tribulations liés au traitement des nombres en virgule flottante.

10
CanSpice

C'est marrant que de nombreux posts mentionnent l'utilisation de BigDecimal mais personne ne s'embarrasse pour donner la réponse correcte basée sur BigDecimal? Parce que même avec BigDecimal, vous pouvez toujours vous tromper, comme en témoigne ce code

String numstr = "1234";
System.out.println(new BigDecimal(numstr).movePointLeft(2));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal(0.01)));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal("0.01")));

Donne cette sortie

12.34
12.34000000000000025687785232264559454051777720451354980468750
12.34

Le constructeur BigDecimal mentionne spécifiquement qu'il vaut mieux utiliser le constructeur String qu'un constructeur numérique. La précision ultime est également influencée par l’option facultative MathContext.

Selon le Javadoc BigDecimal il est possible de créer un BigDecimal qui est exactement égal à 0,1, à condition que vous utilisiez le constructeur String. .

9
Justin Rowe

Oui il y a. Avec chaque double opération, vous risquez de perdre de la précision, mais son degré de précision diffère d'une opération à l'autre et peut être minimisé en choisissant la bonne séquence d'opérations. Par exemple, lorsque vous multipliez un ensemble de nombres, il est préférable de trier cet ensemble par exposant avant de le multiplier.

Tout livre décent sur le calcul en nombre le décrit. Par exemple: http://docs.Oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Et pour répondre à votre question:

Utilisez diviser au lieu de multiplier, ainsi vous obtiendrez un résultat correct.

double x = 1234;
for(int i=1;i<=2;i++)
{
  x =  x / 10.0;
}
System.out.println(x);
5
Jan Kotek

Non, car types à virgule flottante Java (en fait, tous les types à virgule flottante) constituent un compromis entre taille et précision. Bien qu'ils soient très utiles pour beaucoup de tâches, si vous avez besoin d'une précision arbitraire, vous devriez utiliser BigDecimal.

3
biziclop