57 votes

Autour d'un double en Java

J'ai trouvé cette excellente solution pour l'arrondissement:

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 à confusion:

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

Pourquoi est-il en donnant à cette sortie? Je suis l'aide de jre 1.7.0_45.

79voto

user3301492 Points 1087

Vous devez remplacer

BigDecimal bigDecimal = new BigDecimal(d);

avec

BigDecimal bigDecimal = BigDecimal.valueOf(d);

et vous obtiendrez les résultats escomptés:

2.66
1.66

Explication de l'API Java:

BigDecimal.valueOf(double val) - utilise la double canonique de la chaîne de la représentation fournie par le Double.la méthode toString (). C'est la meilleure façon de le convertir en un lit double (ou float) dans un BigDecimal.

new BigDecimal(double val) - utilise exactement la représentation décimale de la double binaire de valeur à virgule flottante et donc les résultats de ce constructeur peut être un peu imprévisible.

26voto

Rahul Tripathi Points 1

Vous pouvez essayer de modifier 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();
}

Exemple De Ideone

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

La raison pourquoi vous êtes obtenir les résultats escomptés avec BigDecimal.valueOf et non pas avec de l' new BigDecimal, dans les mots de Joachim Sauer:

BigDecimal.valueOf(double) utilisera les canonique de la Chaîne de la représentation de la double valeur transmise à instancier le BigDecimal objet. En d'autres termes: La valeur de l' BigDecimal objet sera ce que vous voyez lorsque vous n' System.out.println(d).

Si vous utilisez new BigDecimal(d) cependant, le BigDecimal va essayer de représenter le double de la valeur de façon aussi précise que possible. Ce sera généralement beaucoup plus de chiffres d'être stockée que vous voulez.

Donc, ce qui entraîne une certaine confusion dont vous regardez dans votre programme.

De la Java Doc:

BigDecimal.valueOf(double val) - se Traduit par un double dans un BigDecimal, à l'aide de la double canonique de représentation de chaîne fournis par le Double.toString(double) de la méthode.

new BigDecimal(double val) -

Se traduit par un double dans un BigDecimal qui est l'exact décimal représentation de la double binaire de valeur à virgule flottante. L'échelle de retour de la BigDecimal est la plus petite valeur telle que (10scale × val) est un entier. Notes:

  • Les résultats de ce constructeur peut être un peu imprévisible. On peut supposer que l'écriture de new BigDecimal(0,1) dans Java crée un
    BigDecimal qui est exactement égale à 0,1 (non mis à l'échelle de la valeur de 1,
    avec une échelle de 1), mais il est en fait égal à 0.1000000000000000055511151231257827021181583404541015625. C'est parce que de 0,1 ne peut pas être représenté exactement comme un double (ou, pour que
    la matière, comme une fraction binaire de longueur finie). Ainsi, la valeur
    qui est passé dans le constructeur n'est pas exactement égale à 0.1, malgré les apparences.
  • La Chaîne de constructeur, d'autre part, est parfaitement prévisible: l'écriture de nouvelles BigDecimal("0.1") crée un BigDecimal ce qui est exactement égal à 0,1, que l'on pourrait attendre. Par conséquent, il est généralement recommandé que la Chaîne du constructeur d'être utilisé dans préférence pour celui-ci.
  • Lorsqu'un double doit être utilisé comme source pour un BigDecimal, notez que ce constructeur fournit exacte de conversion; il ne donne pas le
    même résultat que la conversion de la double Chaîne en utilisant la
    Double.toString(double) méthode, puis à l'aide de la BigDecimal(String)
    constructeur. Pour obtenir ce résultat, l'utilisation de la statique valueOf(double)
    la méthode.

18voto

Martijn Courteaux Points 33836

Ce cas de test se termine assez explicite:

public static void main (String[] 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.toPlainString());
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    System.out.println("After round: " + bigDecimal.toPlainString());
    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 sale hack pour corriger ce serait à tour en deux étapes:

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

Ici, 15 est un peu sous le nombre maximal de chiffres un double peut représenter dans la 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

8voto

Abimaran Kugathasan Points 12318

Comme dit dans l'API

  1. Les résultats de ce constructeur peut être un peu imprévisible. On peut supposer que l'écriture de new BigDecimal(0,1) dans Java crée un BigDecimal qui est exactement égale à 0,1 (non mis à l'échelle de la valeur de 1, avec une échelle de 1), mais il est en fait égal à 0.1000000000000000055511151231257827021181583404541015625. C'est parce que de 0,1 ne peut pas être représenté exactement comme un double (ou, pour que la matière, comme une fraction binaire de longueur finie). Ainsi, la valeur qui est passé dans le constructeur n'est pas exactement égale à 0.1, malgré les apparences.

  2. La Chaîne de constructeur, d'autre part, est parfaitement prévisible: l'écriture de nouvelles BigDecimal("0.1") crée un BigDecimal qui est exactement égal à 0,1, que l'on pourrait attendre. Par conséquent, il est généralement recommandé que la Chaîne du constructeur d'être utilisé dans préférence pour celui-ci.

  3. Lorsqu'un double doit être utilisé comme source pour un BigDecimal, notez que ce constructeur fournit exacte de conversion; il ne donne pas le même résultat que la conversion de la double Chaîne en utilisant la Double.toString(double) méthode, puis à l'aide de la BigDecimal(String) constructeur. Pour obtenir ce résultat, l'utilisation de la statique valueOf(double) la méthode.

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

7voto

skiwi Points 8321

Arrondir un double resp Double en elle-même n'a pas beaucoup de sens, en tant que double type de données ne peut pas être arrondis (facilement, ou pas du tout?).

Ce que vous faites est:

  1. Prendre un Double d en entrée et un int precise nombre de chiffres derrière l'élément de séparation.
  2. Créer un BigDecimal de cette d.
  3. Tour de l' BigDecimal correctement.
  4. De retour de l' double de la valeur de l' BigDecimal, ce qui n'a pas d'arrondi appliqué à elle.

Vous pouvez aller de deux façons:

  1. Vous pouvez revenir à une BigDecimal que représente le double arrondi, et décider plus tard de ce que vous faites avec elle.
  2. Vous pouvez revenir à une String représentant les arrondis BigDecimal.

L'un de ces moyens va donner un sens.

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