Un tour de double en Java

J’ai trouvé cette excellente solution pour arrondir:

static Double round(Double d, int precise) { BigDecimal bigDecimal = new BigDecimal(d); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); return bigDecimal.doubleValue(); } 

Cependant, les résultats sont confus:

 System.out.println(round(2.655d,2)); // -> 2.65 System.out.println(round(1.655d,2)); // -> 1.66 

Pourquoi donne-t-il cette sortie? J’utilise jre 1.7.0_45.

Vous devez remplacer

 BigDecimal bigDecimal = new BigDecimal(d); 

avec

 BigDecimal bigDecimal = BigDecimal.valueOf(d); 

et vous obtiendrez les résultats attendus:

 2.66 1.66 

Explication de Java API:

BigDecimal.valueOf (double val) – utilise la représentation de chaîne canonique du double fournie par la méthode Double.toSsortingng (). C’est la méthode préférée pour convertir un double (ou flottant) en BigDecimal.

new BigDecimal (double val ) – utilise la représentation décimale exacte de la valeur à virgule flottante binary du double et les résultats de ce constructeur peuvent donc être quelque peu imprévisibles.

Vous pouvez essayer de changer votre programme comme ceci: –

 static Double round(Double d, int precise) { BigDecimal bigDecimal = BigDecimal.valueOf(d); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); return bigDecimal.doubleValue(); } 

Échantillon d’idéone

 Success time: 0.07 memory: 381184 signal:0 Rounded: 2.66 Rounded: 1.66 Success time: 0.07 memory: 381248 signal:0 Rounded: 2.66 Rounded: 1.66 

Raison pour laquelle vous obtenez le résultat attendu avec BigDecimal.valueOf et non avec le new BigDecimal , selon les mots de Joachim Sauer :

BigDecimal.valueOf(double) utilisera la représentation canonique canonique de la valeur double transmise pour instancier l’object BigDecimal. En d’autres termes: la valeur de l’object BigDecimal sera ce que vous voyez lorsque vous faites System.out.println(d) .

Si vous utilisez new BigDecimal(d) , alors le BigDecimal essaiera de représenter la valeur double aussi précisément que possible. Cela se traduira généralement par plus de chiffres stockés que vous le souhaitez.

D’où une certaine confusion que vous observez dans votre programme.

Du Java Doc:

BigDecimal.valueOf (double val) – Convertit un double en BigDecimal, en utilisant la représentation canonique canonique fournie par la méthode Double.toSsortingng (double).

nouveau BigDecimal (double val)

Convertit un double en BigDecimal, qui est la représentation décimale exacte de la valeur à virgule flottante du double. L’échelle du BigDecimal renvoyé est la plus petite valeur telle que (10 échelles × val) est un entier. Remarques:

  • Les résultats de ce constructeur peuvent être quelque peu imprévisibles. On peut supposer que l’écriture de nouveaux BigDecimal (0.1) en Java crée un
    BigDecimal qui est exactement égal à 0.1 (une valeur non calibrée de 1,
    avec une échelle de 1), mais il est en fait égal à 0.1000000000000000055511151231257827021181583404541015625. C’est parce que 0.1 ne peut pas être représenté exactement comme un double (ou pour cela
    matière, en tant que fraction binary de toute longueur finie). Ainsi, la valeur
    qui est transmis au constructeur n’est pas exactement égal à 0,1, nonobstant les apparences.
  • Le constructeur Ssortingng, par contre, est parfaitement prévisible: l’écriture d’un nouveau BigDecimal (“0.1”) crée un BigDecimal qui est exactement égal à 0.1, comme on peut s’y attendre. Par conséquent, il est généralement recommandé d’utiliser le constructeur Ssortingng de préférence à celui-ci.
  • Lorsqu’un double doit être utilisé comme source pour un BigDecimal, notez que ce constructeur fournit une conversion exacte; il ne donne pas la
    même résultat que la conversion du double en une chaîne en utilisant le
    Double.toSsortingng (double) méthode, puis en utilisant le BigDecimal (Ssortingng)
    constructeur. Pour obtenir ce résultat, utilisez la valeur statique valeurOf (double)
    méthode.

Ce test se révèle assez explicite:

 public static void main (Ssortingng[] args) throws java.lang.Exception { System.out.println("Rounded: " + round(2.655d,2)); // -> 2.65 System.out.println("Rounded: " + round(1.655d,2)); // -> 1.66 } public static Double round(Double d, int precise) { BigDecimal bigDecimal = new BigDecimal(d); System.out.println("Before round: " + bigDecimal.toPlainSsortingng()); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); System.out.println("After round: " + bigDecimal.toPlainSsortingng()); return bigDecimal.doubleValue(); } 

Sortie:

 Before round: 2.654999999999999804600747665972448885440826416015625 After round: 2.65 Rounded: 2.65 Before round: 1.6550000000000000266453525910037569701671600341796875 After round: 1.66 Rounded: 1.66 

Un bidouillage pour le réparer serait de tourner en deux étapes :

 static Double round(Double d, int precise) { BigDecimal bigDecimal = new BigDecimal(d); System.out.println("Before round: " + bigDecimal.toPlainSsortingng()); bigDecimal = bigDecimal.setScale(15, RoundingMode.HALF_UP); System.out.println("Hack round: " + bigDecimal.toPlainSsortingng()); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); System.out.println("After round: " + bigDecimal.toPlainSsortingng()); return bigDecimal.doubleValue(); } 

Ici, 15 est un bit sous le nombre maximum de chiffres qu’un double peut représenter en base 10. Sortie:

 Before round: 2.654999999999999804600747665972448885440826416015625 Hack round: 2.655000000000000 After round: 2.66 Rounded: 2.66 Before round: 1.6550000000000000266453525910037569701671600341796875 Hack round: 1.655000000000000 After round: 1.66 Rounded: 1.66 

Comme dit dans l’ API

  1. Les résultats de ce constructeur peuvent être quelque peu imprévisibles. On pourrait supposer que l’écriture d’un nouveau BigDecimal (0.1) en Java crée un BigDecimal qui est exactement égal à 0.1 (une valeur non calibrée de 1, avec une échelle de 1), mais il est en réalité égal à 0.1000000000000000055511151231257827021181583404541015625. C’est parce que 0.1 ne peut pas être représenté exactement comme un double (ou, par conséquent, comme une fraction binary de toute longueur finie). Ainsi, la valeur transmise au constructeur n’est pas exactement égale à 0,1, nonobstant les apparences.

  2. Le constructeur Ssortingng, par contre, est parfaitement prévisible: l’écriture d’un nouveau BigDecimal (“0.1”) crée un BigDecimal qui est exactement égal à 0.1, comme on peut s’y attendre. Par conséquent, il est généralement recommandé d’utiliser le constructeur Ssortingng de préférence à celui-ci.

  3. Lorsqu’un double doit être utilisé comme source pour un BigDecimal, notez que ce constructeur fournit une conversion exacte; Il ne donne pas le même résultat que la conversion du double en chaîne à l’aide de la méthode Double.toSsortingng (double), puis en utilisant le constructeur BigDecimal (Ssortingng). Pour obtenir ce résultat, utilisez la méthode static valueOf (double).

C’est parce que ne peut pas représenter exactement la valeur double. Vous devez donc utiliser BigDecimal bigDecimal = BigDecimal.valueOf(d); au lieu de BigDecimal bigDecimal = new BigDecimal(d);

Arrondir un double resp Double en soi n’a pas beaucoup de sens, car un double type de données ne peut pas être arrondi (facilement ou pas du tout?).

Ce que vous faites est:

  1. Prenez un Double d en entrée et un nombre int precise de chiffres derrière le séparateur.
  2. Créez un BigDecimal partir de celui-ci.
  3. BigDecimal correctement le BigDecimal .
  4. Renvoie la valeur double de ce BigDecimal , qui n’a plus aucun arrondi .

Vous pouvez aller de deux manières:

  1. Vous pouvez retourner un BigDecimal qui représente le double arrondi et décider plus tard de ce que vous en faites.
  2. Vous pouvez retourner une Ssortingng représentant le BigDecimal arrondi.

Chacune de ces manières aura du sens.

Les nombres décimaux ne peuvent pas être représentés exactement en double.

Donc 2.655 finit par être ceci: 2.65499999999999980460074766597

alors que 1.655 finit par être ceci: 1.655000000000000026645352591