IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

avec Java Discussion :

Appliquer arrondit 2 chiffres après la virgule, sur un calcul à partir d'objets


Sujet :

avec Java

  1. #1
    Membre à l'essai Avatar de BBornier
    Homme Profil pro
    Etudiant en dev Java
    Inscrit en
    Mars 2021
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Etudiant en dev Java

    Informations forums :
    Inscription : Mars 2021
    Messages : 15
    Points : 14
    Points
    14
    Par défaut Appliquer arrondit 2 chiffres après la virgule, sur un calcul à partir d'objets
    Bonjour à tous,
    j'ai un soucis un peu particulier en ce sens où je n'ai pas trouvé de solutions à mes recherches.

    Je dois calculer un prix d'une place de stationnement fonction d'une durée et du prix à l'heure de la palce de parking. Au résultat, j'ai ceci : "Please pay the parking fare:1.50000375". Ce que je souhaite faire c'est avoir 2 chiffres seulement après la virgules.
    Là où est mon problème c'est que la méthode de calcul se fait via un setter/getter et des objets. Ci dessous mon code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    package com.parkit.parkingsystem.service;import java.text.DecimalFormat;
    import com.parkit.parkingsystem.constants.Fare;
    import com.parkit.parkingsystem.model.Ticket;
     
     
    public class FareCalculatorService {
     
        public void calculateFare(Ticket ticket) {
     
               long duration = calculateTicketParkTime(ticket.getOutTime().getTime(), ticket.getInTime().getTime());    
     
               switch (ticket.getParkingSpot().getParkingType()){ // Attribue un type de place de parking (voiture ou vélo)
                case CAR: { // Dans le cas d'une voiture qui se gare
                	//Prend la durée de stationnement, multiplie par le prix du stationnement à l'heure (ici 1.5),
                	//puis la durée en millisecondes est convertit en minutes pour avoir le bon résultat.
                    ticket.setPrice((duration * Fare.CAR_RATE_PER_HOUR)/(60*60*1000)); 
                    break;
                }
                case BIKE: { // Dans le cas d'un vélo qui se gare
                    ticket.setPrice((duration * Fare.BIKE_RATE_PER_HOUR)/(60*60*1000));               
                    break;
                }
                default: throw new IllegalArgumentException("Unkown Parking Type");
     
     
            }
        }
        public long calculateTicketParkTime(long outHour, long inHour) {
     
        	// réadaptation du if de départ.
    		if( (outHour == 0) || (outHour < inHour) ) {
                throw new IllegalArgumentException("Out time provided is incorrect:"+outHour);
            }
        	long duration = outHour - inHour;
    		return duration;
    J'aimerais stocker le résulat de ticket.setPrice((duration * Fare.CAR_RATE_PER_HOUR)/(60*60*1000)); par exemple et utiliser une méthode type DecimalFormat pour que j'obtienne juste 2 chiffres après la décimale. Mais je ne vois pas comment faire.
    Je pourrais créer une méthode que je pourrai appeler dans mon switch mais là aussi je suis coincé..

    Une idée ?
    En vous remerciant

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Hello,

    le code semble indiquer que le prix est stocké dans une variable de type float ou double.

    Le problème de ces type, c'est qu'ils ne peuvent pas stocker de nombre avec deux chiffres après la virgule. Ils ne calculent que des approchés.
    (La raison en est que quand on dit "deux chiffres après la virgule" on veut dire en notation humaine, écrits avec 10 chiffres. Or les ordinateurs utilisent le binaire.)

    Pour stocker un prix en centimes, de sorte que ce soit une valeur exacte, il faudrait donc utiliser autre chose.

    Plusieurs possibilités :

    - Une String, donc oui, à l'aide d'un DecimalFormat
    - Un BigDecimal, qu'on arrondit aux centièmes
    - Un int ou un long, qui contient le prix en centimes. Penser à convertir en euros avant l'affichage, du coup il faudra quand même passer par String ou BigDecimal quand on veut afficher.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Membre à l'essai Avatar de BBornier
    Homme Profil pro
    Etudiant en dev Java
    Inscrit en
    Mars 2021
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Etudiant en dev Java

    Informations forums :
    Inscription : Mars 2021
    Messages : 15
    Points : 14
    Points
    14
    Par défaut
    Hello thelvin,

    Merci pour ta réponse ! donc je suis pas trop mal et sur la bonne voie.
    Le prix est effectivement un double :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public double getPrice() {
            return price;
        }
     
        public void setPrice(double price) {
            this.price = price;
        }
    Le problème quand je souhaite utiliser DecimalFormat :
    Dois-je utiliser la méthode de la manière suivante ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    switch (ticket.getParkingSpot().getParkingType()){ 
                case CAR: { 
                    double price = ticket.setPrice((duration * Fare.CAR_RATE_PER_HOUR)/(60*60*1000)); 
                    DecimalFormat df = new DecimalFormat("#.##");
                	df.format(price);
                    break;
    Il m'envoie un peu dans les pâquerettes..

  4. #4
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Hum, comme dit au-dessus, un double ne peut pas contenir un nombre "à deux virgules", donc le prix ne peut pas être un double. Il faut choisir une des trois options.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre à l'essai Avatar de BBornier
    Homme Profil pro
    Etudiant en dev Java
    Inscrit en
    Mars 2021
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Etudiant en dev Java

    Informations forums :
    Inscription : Mars 2021
    Messages : 15
    Points : 14
    Points
    14
    Par défaut
    Ok, je pensais que je pouvais d'abord déclarer mon price double puis le convertir grâce à la méthode DecimalFormat().

    Mais du coup comment stocker le résultat du calcul (duration * Fare.CAR_RATE_PER_HOUR)/(60*60*1000)) ? pour pouvoir ensuite l'utiliser avec DecimalFormat() ? Car cette méthode n'accepte pas le type de paramètres du calcul inclus dans le setter setPrice().

  6. #6
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Avec une variable.

    Pardon j'avais oublié qu'on est dans la section débuter.

    On peut faire les choses de cette manière :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    double theory = computeTheoreticalPrice();
     
    String rounded = roundPrice(theory);
     
    object.setPrice(rounded);
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  7. #7
    Membre à l'essai Avatar de BBornier
    Homme Profil pro
    Etudiant en dev Java
    Inscrit en
    Mars 2021
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Etudiant en dev Java

    Informations forums :
    Inscription : Mars 2021
    Messages : 15
    Points : 14
    Points
    14
    Par défaut
    Citation Envoyé par thelvin Voir le message
    Avec une variable.

    Pardon j'avais oublié qu'on est dans la section débuter.
    C'est ça

    Je vais tester ça du coup ^^. je reviens vers toi si jamais . Merci !

  8. #8
    Membre à l'essai Avatar de BBornier
    Homme Profil pro
    Etudiant en dev Java
    Inscrit en
    Mars 2021
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Etudiant en dev Java

    Informations forums :
    Inscription : Mars 2021
    Messages : 15
    Points : 14
    Points
    14
    Par défaut
    Bonjour,

    Alors du coup je reviens avec ma solution, ça peut toujours servir.

    J'ai adapté mon getter de la manière suivante avec BigDecimal :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    public double getPrice() {
        	 BigDecimal bd = new BigDecimal(price);
        	 bd = bd.setScale(2, RoundingMode.HALF_DOWN);
        	 price = bd.doubleValue();
            return price;
        }
    j'ai donc bien mes arrondit en résultat sans problème. Tout fonctionne à merveille.

    En revanche j'ai un nouveau problème que je n'avais pas avant : C'est que sur un calcul simple mon IDE (Eclipse) me donne maintenant de manière incompréhensible je ne sais combien de chiffres après la virgule. Et je ne vois pas en quoi mon getter influencerait mon test JUnit sur un calcul des plus basiques !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    assertEquals( (0.58 * Fare.CAR_RATE_PER_HOUR) , ticket.getPrice());
            System.out.println(ticket.getPrice());
            System.out.println((0.58 * Fare.CAR_RATE_PER_HOUR));
    Dans ma classe Fare (prix de stationnement à l'heure d'une place de parking pour une voiture ou une moto), le prix à l'heure est de 1.5 pour CAR.
    Résultat en console de mon ticket.getPrice : 1.25. Donc pas de problème.
    Résultat en console du calcul de base du test (0.83*1.5) : 1.2449999999999999. Mais WTF ?? Je devrais avoir au moins comme résultat 1.245.

    Euh... peut-on éclairer ma lanterne ? Merci

  9. #9
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Hello,

    comme je t'ai dit, les double ne sont pas capables de contenir des nombres avec deux chiffres après la virgule, et par conséquent le prix ne peut pas être contenu dans un double.

    Pourtant je vois ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public double getPrice() {
    .... Il va falloir effacer et recommencer.

    Tu dois te débrouiller pour que quand tu as fini ça ressemble à ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public mystère getPrice() {
    Où je te laisse comprendre ce qu'il faut mettre à la place de "mystère", comme déjà dit tu as plusieurs possibilités, mais en tout cas ce ne sera jamais, jamais, "double".
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  10. #10
    Membre à l'essai Avatar de BBornier
    Homme Profil pro
    Etudiant en dev Java
    Inscrit en
    Mars 2021
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Etudiant en dev Java

    Informations forums :
    Inscription : Mars 2021
    Messages : 15
    Points : 14
    Points
    14
    Par défaut
    Alors je vois bien ce que tu veux me dire, je vais sans doute paraître un peut têtu, mais j'essaie de comprendre :

    - Sur mon getter (même si le type est noté "double"), comment se fait-il qu'en utilisant la méthode BigDecimal, mes arrondits sur mes résultats se passent bien ? Quand je lance mon application, le prix est bien généré avec 2 ou 3 chiffres après la virgule comme je le souhaite (le véhicule entre à une heure et date donnée, il en sort avec une heure et date donnée ultérieure. le calcul est paramétré dans une classe et méthodes en faisant la différence entre l'heure de sortie et l'heure d'entrée que multiplie par le prix du stationnement à l'heure).

    - sur un calcul qui avant se passait très bien (je ne sais pas bien ce qui s'est passé à vrai dire) le programme me génère à présent je ne sais combien de chiffres après la virgule..

    Ma classe Fare se présente comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    package com.parkit.parkingsystem.constants;
     
    public class Fare {
        public static final double BIKE_RATE_PER_HOUR = 1.0;
        public static final double CAR_RATE_PER_HOUR = 1.5;
    }
    Est-ce que ça viendrait de cela du coup ? et faudrait-il que j'utilise ici BigDecimal(), puis ensuite changer le type de mon setter et getter pour le remplacer comme tu dis par le type sur lequel tu veux m'orienter ?

  11. #11
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Pas de problème, c'est une question intéressante.

    Sur mon getter (même si le type est noté "double"), comment se fait-il qu'en utilisant la méthode BigDecimal, mes arrondits sur mes résultats se passent bien ?
    Ils ne se passent pas bien.

    Comme déjà dit, les double ne peuvent pas contenir de nombre avec deux chiffres après la virgule. Il en résulte donc :

    • Ou bien tu crois cette information quand elle t'est donnée, sans avoir besoin de poser plus de questions
    • Ou bien tu penses qu'il est possible, dans ce monde, que l'arrondi décrit au-dessus se passe bien


    C'est soit l'un, soit l'autre.

    J'ai déjà expliqué que l'impossibilité vient de ce que les ordinateurs comptent en binaire, et que quand on dit "deux chiffres après la virgule" on veut dire en convention humaine c'est à dire en base 10. La base 10 c'est 2x5. Tant qu'on n'utilise que des diviseurs de 2, c'est faisable en binaire, mais dès qu'il y a du diviseur de 5 là il n'y a plus rien à faire.

    Du coup je ne vois pas ce qu'il reste à dire pour comprendre que les arrondis ne peuvent pas s'être bien passé.

    Ce qu'il y a eu, c'est que tu as eu l'impression, en voyant de la situation ce que tu as pu en observer, que ça c'était bien passé. Mais c'est seulement parce que tu n'as observé que peu de choses de la situation. Mythe de la caverne, tout ça.

    Un double ne peut pas contenir de nombre avec deux chiffres après la virgule, mais rien ne l'empêche de contenir une valeur très proche de la valeur d'un nombre qui a deux chiffres après la virgule.
    L'affichage avec System.out.println() n'étant qu'à titre informatif dans la mesure où on ne précise rien sur la façon dont on veut afficher le double, qui n'a pourtant pas de forme d'affichage canonique,
    eh bien cet affichage n'affiche qu'une précision limitée. En fait, c'est fait pour afficher autant de chiffres après la virgule, que nécessaire pour qu'on ne puisse pas le confondre avec un double qui aurait une autre valeur. Parfois il faut beaucoup de chiffres, et parfois peu.

    Donc, avec ton arrondi, tu as produit des doubles qui ont une valeur très proche de la valeur que tu voulais obtenir, mais une valeur différente de la valeur que tu voulais obtenir.

    En ne vérifiant qu'avec System.out.println(), il était possible que cela se remarque, et probable que ça ne se remarque pas, puisqu'il ne sert pas à ça.

    Mais en utilisant le double obtenu d'autres manières, et surtout en faisant plus de calculs avec, la différence entre la valeur que tu voulais obtenir et la valeur que tu as vraiment, continue de s'appliquer, et peut causer encore plus de différences entre résultats attendus et résultats obtenus. L'exactitude n'existe que du début à la fin. Dès lors qu'on introduit une valeur inexacte (bien que très proche) dans un calcul, le résultat sera inexact (mais très proche du résultat exact).

    Ma classe Fare se présente comme ceci :
    Je ne remarque rien de particulier là-dedans à part qu'il se trouve que les valeurs 1.0 et 1.5 peuvent, elles, être représentées exactement dans des double. Du coup, il peut arriver par hasard que certains calculs faits avec donnent des résultats exact.

    Le truc en informatique, c'est qu'on évite de raisonner en "il peut arriver par hasard qu'on arrive au bon résultats si les conditions arbitraires sont réunies sans qu'on ait cherché à ce qu'elles le soient" mais plutôt en "on doit obtenir le résultat exact quoi qu'il arrive".

    et faudrait-il que j'utilise ici BigDecimal(), puis ensuite changer le type de mon setter et getter pour le remplacer comme tu dis par le type sur lequel tu veux m'orienter ?
    Ben c'est à dire que si tu utilises BigDecimal, logiquement le type du prix ça devrait être BigDecimal.

    Et forcément les getters et setters du prix, aussi.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  12. #12
    Membre à l'essai Avatar de BBornier
    Homme Profil pro
    Etudiant en dev Java
    Inscrit en
    Mars 2021
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Etudiant en dev Java

    Informations forums :
    Inscription : Mars 2021
    Messages : 15
    Points : 14
    Points
    14
    Par défaut
    Du coup forcément ça éclaire beaucoup mieux ma lanterne Merci, c'est super intéressant !

    Pour ce qui est de ce sujet, et pour rejoindre l'ensemble de ta réflexion, ce que je veux, comme c'est un calcul de prix, c'est que cela soit le plus précis possible.
    Comme dans notre monde on est à 2 chiffres après la virgule pour un paiement (avec arrondit). L'idée c'est de tronquer juste ce dont je n'ai pas besoin, et obtenir un tarif comme lorsqu'on va payer sa place de stationnement dans un parking tout ce qu'il y a de plus trivial.

    Du coup je me dit que dans la classe Fare{} je pourrais donc tout définir en BigDecimal directement. Ce qui me poussera à utiliser la méthode multiply() si jeux faire des calculs avec au vu de la JavaDoc. ça va bousculer l'ensemble de l'appli, mais apparemment, pas bien le choix ^^.


    Merci pour tout ces éclaircissements .

  13. #13
    Modérateur
    Avatar de wax78
    Homme Profil pro
    Chef programmeur
    Inscrit en
    Août 2006
    Messages
    4 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chef programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 4 074
    Points : 7 978
    Points
    7 978
    Par défaut
    Mais si il y'a le choix.

    Tu calcules tout avec des double ou des float, et au moment de présenter les chiffres à l'utilisateur tu les formattes.
    Qu'il doive paye 25.00001€ ou 24.9999998€ pour son parking, il payera 25.00 €, c'est impossible de payer en dessous de 2 décimales
    Ce n'est pas un calcul insurmontable et compliqué donc utiliser des bigdecimal me semble inadéquat avec des petits chiffres de cet ordre.

    (cependant les remarques de Thelvin restent vraies)
    (Les "ça ne marche pas", même écrits sans faute(s), vous porteront discrédit ad vitam æternam et malheur pendant 7 ans)

    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  14. #14
    Membre à l'essai Avatar de BBornier
    Homme Profil pro
    Etudiant en dev Java
    Inscrit en
    Mars 2021
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Etudiant en dev Java

    Informations forums :
    Inscription : Mars 2021
    Messages : 15
    Points : 14
    Points
    14
    Par défaut
    ça roule ^^ j'en prends bonne note également !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. arrondir un nombre à 2 chiffres après la virgule
    Par El Saigneur dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 01/02/2005, 08h36
  2. Commande pour afficher des chiffres apres la virgule
    Par manar dans le forum Shell et commandes GNU
    Réponses: 2
    Dernier message: 23/09/2004, 18h28
  3. Chiffres après la virgule figés
    Par bondjames dans le forum Bases de données
    Réponses: 6
    Dernier message: 10/03/2004, 23h09
  4. [MFC] Nombre de chiffres après la virgule
    Par karl3i dans le forum MFC
    Réponses: 3
    Dernier message: 27/01/2004, 13h04
  5. Nb de chiffres après la virgule ?
    Par Thcan dans le forum C
    Réponses: 10
    Dernier message: 17/09/2003, 21h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo