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

JavaScript Discussion :

troncage d'un montant avec floor et écart de centime.


Sujet :

JavaScript

  1. #21
    Rédacteur/Modérateur

    Avatar de SpaceFrog
    Homme Profil pro
    Développeur Web Php Mysql Html Javascript CSS Apache - Intégrateur - Bidouilleur SharePoint
    Inscrit en
    Mars 2002
    Messages
    39 640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 74
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur Web Php Mysql Html Javascript CSS Apache - Intégrateur - Bidouilleur SharePoint
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2002
    Messages : 39 640
    Points : 66 663
    Points
    66 663
    Billets dans le blog
    1
    Par défaut
    le souci vient du toString() et du lengtt qui retourne 3 donc power= 3


    et hop :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var montantHT=60
    var test2= "1.1959999999999999"
    var power=test2.match(/\.(\d+)$/)[1].length
    var foo=  Math.round(montantHT*test2*Math.pow(10,power))/Math.pow(10,power)
     
    alert((foo))

  2. #22
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    Citation Envoyé par SpaceFrog Voir le message
    le souci vient du toString() et du lengtt qui retourne 3 donc power= 3


    et hop :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var montantHT=60
    var test2= "1.1959999999999999"
    var power=test2.match(/\.(\d+)$/)[1].length
    var foo=  Math.round(montantHT*test2*Math.pow(10,power))/Math.pow(10,power)
     
    alert((foo))
    ça ne fonctionne plus du tout sans le toString() (sous chrome).

    mais bon, de toute façon, j'ai retiré mon "contre exemple" qui provient juste de l'erreur du moteur javascript et non pas de ta solution.

    de toute façon, je crois qu'on a fait le tour de la question et que ta solution fonctionne aussi. (jusqu'a preuve du contraire *gniark gniark* )


    ps: j'crois qu'on a fait fuir tout le monde avec nos solutions et tests, même l'auteur du sujet.


    edit: juste par curiosité, pourquoi la double parenthèse à "alert((foo))" ?

  3. #23
    Rédacteur/Modérateur

    Avatar de SpaceFrog
    Homme Profil pro
    Développeur Web Php Mysql Html Javascript CSS Apache - Intégrateur - Bidouilleur SharePoint
    Inscrit en
    Mars 2002
    Messages
    39 640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 74
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur Web Php Mysql Html Javascript CSS Apache - Intégrateur - Bidouilleur SharePoint
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2002
    Messages : 39 640
    Points : 66 663
    Points
    66 663
    Billets dans le blog
    1
    Par défaut
    non la double parenthèse ne sert à rien ..

    et je viens de tester sous chrome ça passe nickel
    à noter que je n'ai pas fait que virer le toString
    test2 est en alpha ...

  4. #24
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    Citation Envoyé par SpaceFrog Voir le message
    non la double parenthèse ne sert à rien ..

    et je viens de tester sous chrome ça passe nickel
    à noter que je n'ai pas fait que virer le toString
    test2 est en alpha ...
    (j'avais pas vu l'apha)

    et ta solution est bien meilleure que la mienne ! (qui ne fonctionne en fait que dans ce cas présent)

  5. #25
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 075
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 075
    Points : 44 657
    Points
    44 657
    Par défaut
    indéniablement en Informatique il faut oublier que la multiplication est commutative, et pourtant j'en ai ch#$?r avant de comprendre à quoi cela pouvait servir.

    donc il faut retenir que, merci Willpower
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    var Result = Nbr * Mul * 100;
    est moins précis que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    var Result = 100 * Nbr * Mul;
    je glisse au passage qu'il n'est pas judicieux de passer par des calculs intermédiaires
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    var valTemp = Nbr * Mul;
    var Result = valTemp * 100;
    dans le cas d'utilisation de la division / multiplication le résultat sera toujours plus grand avec la division, et plus souvent "précis" mais aucune garantie que cela soit vrai sur toutes les plateformes, c'est pour cette raison que je proposer de garder le plus grand. Ceci est valable que l'on fasse (100 * Nb * Mul) ou (nb * Mul * 100).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    var Mul = 1.196;
    var Div = 1 / Mul;
    var i, valMul, valDiv;
    for(  i = 1; i < 100; i +=1){
      valMul = i * Mul * 100;
      valDiv = i / Div * 100;
      document.write( (valMul > valDiv) + '<br>');
    }
    en fait cela se résume à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    var Taux = 1.196;
    var Div = 1 / Taux;
    var montantHT = 60;
    var montantTTC = Math.floor( 100 * montantHT /Div) /100;
    Citation Envoyé par Willpower
    mais ça devient presque aussi tordu que la solution de noSmoking
    pas tant que cela donc.

    Vivement le retour des nombres en virgule fixe.


    PS
    ps: j'crois qu'on a fait fuir tout le monde avec nos solutions et tests, même l'auteur du sujet.
    non juste le boulot qui ne laisse pas toujours des trêves

  6. #26
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    c'est totalement n'importe quoi en fait :

    Code js : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    var nbr = 600000000000;
    for(var i=0;i<20;i++)
    	document.write((nbr/=10)*1.196,'<br/>');

    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
    71760000000
    7176000000
    717600000
    71760000
    7176000
    717600
    71760
    7176
    717.6
    71.75999999999999
    7.176
    0.7175999999999999
    0.07175999999999999
    0.007176
    0.0007176
    0.00007176
    0.000007176000000000001
    7.176e-7
    7.176e-8
    7.176000000000001e-9

    mais le pire de tout, c'est que les résultats seront TOTALEMENT IDENTIQUES sur chrome, firefox et internet explorer !

  7. #27
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 075
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 075
    Points : 44 657
    Points
    44 657
    Par défaut
    Citation Envoyé par Willpower Voir le message
    c'est totalement n'importe quoi en fait :

    Code js : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    var nbr = 600000000000;
    for(var i=0;i<20;i++)
    	document.write((nbr/=10)*1.196,'<br/>');
    ...

    mais le pire de tout, c'est que les résultats seront TOTALEMENT IDENTIQUES sur chrome, firefox et internet explorer !
    nous sommes tous bien conscient que le calcul en virgule flottante donne des résultats à précision limitée.

    Remettons nous dans le contexte de la question, ce qui est demandé est la troncature du résultat au centième, donc il faut tester sur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      val = Math.floor( nbr /div *100)/100;
    c'est donc avec ce test qu'il faut comparer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var mul = 1.196;
    var div = 1/mul;
    var nbr = 600000000000;
    var i, val, pre = 100;
    for( i=0;i<20;i++){
      nbr /= 10;
      val = Math.floor( nbr /div *pre)/pre;    // Ok
      val = Math.floor( pre * nbr /div )/pre;  // Ok
      val = Math.floor( nbr * mul *pre)/pre;   // Nok pour i = 9
      val = Math.floor( pre * nbr *mul)/pre;   // Ok
      document.write( i ,' -> ', val,'<br/>');
    }

  8. #28
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    Citation Envoyé par NoSmoking Voir le message
    nous sommes tous bien conscient que le calcul en virgule flottante donne des résultats à précision limitée.

    Remettons nous dans le contexte de la question, ce qui est demandé est la troncature du résultat au centième, donc il faut tester sur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      val = Math.floor( nbr /div *100)/100;
    c'est donc avec ce test qu'il faut comparer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var mul = 1.196;
    var div = 1/mul;
    var nbr = 600000000000;
    var i, val, pre = 100;
    for( i=0;i<20;i++){
      nbr /= 10;
      val = Math.floor( nbr /div *pre)/pre;    // Ok
      val = Math.floor( pre * nbr /div )/pre;  // Ok
      val = Math.floor( nbr * mul *pre)/pre;   // Nok pour i = 9
      val = Math.floor( pre * nbr *mul)/pre;   // Ok
      document.write( i ,' -> ', val,'<br/>');
    }
    le premier test foire aussi pour nbr = 10 et mul = 1.8 et pre=100

    on devrait se lancer dans la généralisation des cas qui fonctionnent et ceux qui foirent voir même créer une fonction qui lancer les 4 cas (ou plus) par exemple et retourne le resultat qui revient le plus souvent ... ou alors lance une fonction plus élaborée si les 4 résultats ne sont pas identiques .. ou un truc du genre (2 prétests seulement pour la rapidité par exemple)...


    chaud de te lancer avec moi dans l'élaboration de cette fonction "floatEval" ?

  9. #29
    Rédacteur

    Avatar de danielhagnoul
    Homme Profil pro
    Étudiant perpétuel
    Inscrit en
    Février 2009
    Messages
    6 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 74
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant perpétuel
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Points : 22 933
    Points
    22 933
    Billets dans le blog
    125
    Par défaut
    Bonsoir

    Pour mon code, je cherche simplement à obtenir le résultat exact. De supprimer les problèmes d'arrondis autrement dit.

    Après avoir testé avec différentes valeurs, je pense que l'on peut l'améliorer comme ci-dessous.

    Mais je ne suis pas très fort en RegExp, alors j'attends l'avis de @SpaceFrog.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var montantHT = 10, //10, 0.1, 0.0001,
    	test2 = 118, //118, 1.18, 0.000118,
    	d1 = (montantHT.toString().match(/\.(\d+)$/)) ? (montantHT.toString().match(/\.(\d+)$/)[1]) : (""),
    	d2 = (test2.toString().match(/\.(\d+)$/)) ? (test2.toString().match(/\.(\d+)$/)[1]) : (""),
    	power = d1.length + d2.length,
    	foo = Math.round(montantHT * test2 * Math.pow(10, power))/Math.pow(10, power);
     
    console.log(montantHT, ", ", test2, ", ",  d1, ", ", d2, ", ", power, ", ", foo);
     
    var x = montantHT * test2;
     
    console.log(x);
    ---------------------------------------
    [Edit 2011-03-26 20:45]

    On ne teste jamais assez, le résultat est exact souvent, mais pas tout le temps. À partir du moment où l'on utilise Math.round(), directement ou indirectement avec toFixed(), le résultat n'est pas valable dans 100*% des cas. De plus, j'ai besoin de 15 décimales exactes au minimum pour mes calculs.

  10. #30
    Expert confirmé
    Avatar de javatwister
    Homme Profil pro
    danseur
    Inscrit en
    Août 2003
    Messages
    3 681
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : danseur

    Informations forums :
    Inscription : Août 2003
    Messages : 3 681
    Points : 5 221
    Points
    5 221
    Par défaut
    zut, marche plus...

  11. #31
    Rédacteur

    Avatar de danielhagnoul
    Homme Profil pro
    Étudiant perpétuel
    Inscrit en
    Février 2009
    Messages
    6 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 74
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant perpétuel
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Points : 22 933
    Points
    22 933
    Billets dans le blog
    125
    Par défaut
    Bonjour

    @javatwister : arrondir au centième, donc

    Pour mon code, je cherche simplement à obtenir le résultat exact. De supprimer les problèmes d'arrondis autrement dit.

    ---------------------------------------
    [Edit 2011-03-26 20:45]

    On ne teste jamais assez, le résultat est exact souvent, mais pas tout le temps. À partir du moment où l'on utilise Math.round(), directement ou indirectement avec toFixed(), le résultat n'est pas valable dans 100*% des cas. De plus, j'ai besoin de 15 décimales exactes au minimum pour mes calculs.

  12. #32
    Expert confirmé
    Avatar de javatwister
    Homme Profil pro
    danseur
    Inscrit en
    Août 2003
    Messages
    3 681
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : danseur

    Informations forums :
    Inscription : Août 2003
    Messages : 3 681
    Points : 5 221
    Points
    5 221
    Par défaut
    bon allez hop, fin de ma prise de tête, j'étais parti nulle part ^^

  13. #33
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 075
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 075
    Points : 44 657
    Points
    44 657
    Par défaut
    Citation Envoyé par moi même
    dans le cas d'utilisation de la division / multiplication le résultat sera toujours plus grand avec la division, et plus souvent "précis" mais aucune garantie que cela soit vrai sur toutes les plateformes,
    ce qui est écrit n'est pas valable dans pas mal de cas et ne dépend en fait en rien de la plateforme mais très largement du multiplicateur, avec 1.273 la multiplication rend plus souvent le résultat "vrai".

    Première conclusion il faut donc bien garder le plus grand, sauf que cela foire aussi de temps à autre.

    Conclusion : on jette !!!! si l'on veut généraliser.

    Une autre approche est de partir sur un résultat de la multiplication exacte, oui mais comment

    Approche :
    dans une mutiplication le nombre de décimale en final est égal, au plus, à la somme des décimales des deux nombres à multiplier
    - 12.1 * 1.926 = 23.3046 donc ( 1 +3) = 4 décimales
    - 12.05 * 1.926 = 23.2083 donc( 2 +3) = 5 décimales, ici 4 les zéros de droite n'étant pas significatifs.
    il faut donc créer une fonction de multiplication qui renvoie le pil poil recherché en fonction du nombre de décimale, par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function __mul( val1, val2){
      // decomposition
      var valeur1 = val1.toString().split('.');
      var valeur2 = val2.toString().split('.');
      // nombre de decimal a traiter
      var decimal = ( valeur1[1] ? valeur1[1].length : 0);
      decimal    += ( valeur2[1] ? valeur2[1].length : 0);
      // calcul normal
      var Result = val1 * val2;
      // retour fixed au nombre de decimale
      return ( parseFloat( Result.toFixed( decimal)));
    }
    maintenant que cela est fait la truncature pose également problème sous sa forme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Math.floor( 100 * montantHT /Div) /100;
    car on utilise une multiplication standard.
    En utilisant une multiplication _mul,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // utlisation de _mul
    valMul = __mul( i, Mul);
    // utlisation de _mul pour truncature
    valTruncMul = Math.floor( __mul( pre , valMul))/pre;
    il est encore des cas qui sont erronés, donc toujours pas vraiment satisfaisant

    Une solution consiste donc à faire une fonction de truncage qui elle aussi va tenir compte du nombre de décimale du nombre en entrée, je suis arrivé à cela
    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
    function __truncate( val, rang){
      // pas de valeur neg
      if( rang < 0) return( val);
     
      var nombre, tab;
      // decomposition
      var valeur = val.toString().split('.');
      // nombre de decimales presentent
      var decimal = ( valeur[1] ? valeur[1].length : 0);
      // allonge la representation du nombre
      // probleme truncate si decimale inferieure a precision
      if( decimal < rang){
        nombre = val.toFixed( rang);
      }
      else{
        nombre = val.toString();
      }
      // redecomposition
      valeur = nombre.toString().split('.');
      // truncate la partie gauche
      tab = valeur[1].split('');
      tab.length = rang;
      // recompose
      valeur[1] = tab.join('');
      // on retourne le floattant
      return ( parseFloat( valeur.join('.')));
    }
    et là ... je n'ai pas mis en évidence d'erreur dans les résultats, mais les tests ne sont pas exhaustifs il va de soit.

    Je vous mets le code du fichier test
    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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Mutiplication et Truncature</title>
    <style type="text/css">
    *{
      font-family : Verdana;
    }
    table {
      border : 1px solid #e0e0e0;
      empty-cells : show;
    }
    th {
      border : 1px solid #e0e0e0;
      font-size : 13px;  
    }
    td {
      border : 1px solid #e0e0e0;
      font-size : 13px;  
    }
    i {
       color : #00f;
       font-size : 13px;
    }
    u {
       color : #f00;
       font-size : 13px;
       text-decoration : none;
    }
    </style>
    <script type="text/javascript">
    function __mul( val1, val2){
      // decomposition
      var valeur1 = val1.toString().split('.');
      var valeur2 = val2.toString().split('.');
      // nombre de decimal a traiter
      var decimal = ( valeur1[1] ? valeur1[1].length : 0);
      decimal    += ( valeur2[1] ? valeur2[1].length : 0);    
      // calcul normal
      var Result = val1 * val2;
      // retour fixed au nombre de decimale
      return ( parseFloat( Result.toFixed( decimal)));
    }
     
    function __truncate( val, rang){
      // pas de valeur neg
      if( rang < 0) return( val);
     
      var nombre, tab;
      // decomposition
      var valeur = val.toString().split('.');
      // nombre de decimales presentent
      var decimal = ( valeur[1] ? valeur[1].length : 0);
      // allonge la representation du nombre
      // probleme  truncate si decimale inferieure a precision
      if( decimal < rang){
        nombre = val.toFixed( rang);
      }
      else{
        nombre = val.toString();
      }
      // redecomposition
      valeur = nombre.toString().split('.');
      // truncate la partie gauche
      tab = valeur[1].split('');
      tab.length = rang;
      // recompose
      valeur[1] = tab.join('');
      // on retourne le floattant
      return ( parseFloat( valeur.join('.')));
    }
    //////////////////////////////////////////
    var valMul, valDiv, _valMul;
    var valTruncMul, valTruncDiv, _valTruncMul, __valTruncMul, _valTruncate;
     
    var Mul = 1.118;
    //var Mul = 1.196;
    //var Mul = 1.273; //cas faillible
    var Div = 1 / Mul;
     
    // partie multiplicateur
    var dec = 3;
    var pre = Math.pow(10, dec);
     
    var i, ind = 1;
    var Html = ['<table><tr>'];
    Html[ind++] = '<th>i<\/th>';
    Html[ind++] = '<th>__mul<\/th>';
    Html[ind++] = '<th>i/Div<\/th>';
    Html[ind++] = '<th>i*Mul<\/th>';
    Html[ind++] = '<th>TruncDiv<br>1<\/th>';
    Html[ind++] = '<th>TruncMul<br>2<\/th>';
    Html[ind++] = '<th>Ecart<br>1-2<\/th>';
    Html[ind++] = '<th>_TruncMul<br>3<\/td>';
    Html[ind++] = '<th>__TruncMul<br>4<\/td>';
    Html[ind++] = '<th>Ecart<br>3-4<\/th>';
    Html[ind++] = '<th>Truncate<br>5<\/td>';
    Html[ind++] = '<th>Ecart<br>5-2<\/th>';
    Html[ind++] = '<\/tr>';
     
    for( i = 1; i < 100; i += 1){
      // calcul traditionnel
      valMul = i*Mul;
      valDiv = i/Div;
      valTruncMul = Math.floor( pre * i*Mul)/pre;
      valTruncDiv = Math.floor( pre * i/Div)/pre;
      // utlisation de _mul
      _valMul = __mul( i, Mul);
      _valTruncMul  = Math.floor( pre * _valMul)/pre;
      // utlisation de _mul pour truncature
      __valTruncMul = Math.floor( __mul( pre , _valMul))/pre;
      // truncature maison !
      _valTruncate =  __truncate( _valMul, dec);
     
      Html[ind++] = '<tr>';
      Html[ind++] = '<td>' +i +'<\/td>';
      Html[ind++] = '<td><i>' +_valMul +'<\/i><\/td>';
      Html[ind++] = '<td>' +valDiv  +'<\/td>';
      Html[ind++] = '<td>' +valMul  +'<\/td>';
     
      Html[ind++] = '<td>' +valTruncDiv  +'<\/td>';
      Html[ind++] = '<td>' +valTruncMul  +'<\/td>';
      Html[ind++] = '<td><u>' +(valTruncDiv -valTruncMul) +'<\/u><\/td>';
     
      Html[ind++] = '<td><i>' +_valTruncMul +'<\/i><\/td>';
      Html[ind++] = '<td><i>' +__valTruncMul +'<\/i><\/td>';
      Html[ind++] = '<td><u>' +(_valTruncMul -__valTruncMul) +'<\/u><\/td>';
     
      Html[ind++] = '<td><i>' + _valTruncate +'<\/i><\/td>';
      Html[ind++] = '<td><u>' +(_valTruncate - valTruncMul) +'<\/u><\/td>';
      Html[ind++] = '<\/tr>';
    }
    Html[ind++] ='<\/table>';
    document.write( Html.join(''));
    </script>
    </head>
    <body>
    </body>
    </html>
    Il reste qu'il doit y avoir moyen de faire peut être plus simple et/ou compact

  14. #34
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    une autre approche consiste à remplacer la multiplication flottante par une multiplication entière en supprimant la virgule, et à la replacer après le calcul effectué. pas d'erreur possible de cette manière.
    (seul risque possible : générer un nombre "trop grand" ? mais d'après la variable Number.MAX_VALUE, mes versions respectives d'IE, Chrome et Firefox pourraient gérer des entiers long de 308 chiffres donc à priori pas de soucis à ce niveau là. )

    function:
    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
    37
    38
    function multi(n1,n2){
    	if(typeof n1 === 'string') {
    		// découpe des valeurs du string à chaque *
    		var arg = n1.split("*");
    		// conversion des "number" et vars en number
    		for(var i=0;i<arg.length;i++)
    			arg[i] = eval(arg[i]); 
    		// appel récursif avec des valeurs entières dans chaque paramètre
    		return multi.apply(this,arg);
    	}
    	// appel récursif si plus de 2 paramètres
    	if(arguments.length>2)
    		n2 = multi.apply(this,[].slice.call(arguments,1)); 
    	// var temporaire
    	var tmp;
    	// nombre de chiffres derrière les virgules
    	var len1 = (tmp = n1.toString().split('.')[1])?tmp.length:0; 
    	var len2 = (tmp = n2.toString().split('.')[1])?tmp.length:0; 
    	// total de la longueur des virgules
    	var len = len1 + len2;
    	// suppression des virgules
    	var int_n1 = parseInt(n1.toString().replace('.',''),10);
    	var int_n2 = parseInt(n2.toString().replace('.',''),10);
    	// ajout de "000" devant le result pour prévenir des valeurs inférieures à 0
    	var zero = "0";
    	for(var i=1;i<=len;i*=2)
    		zero += zero;
    	// produit sans virgules (format:string)
    	var result =  zero+(int_n1*int_n2 ).toString();
    	// divsion du result en 2 parties : devant et derrière la virgule
    	var before = result.substr(0,result.length-len); 
    	var after = result.substr(result.length-len,result.length); 
    	// rajout de la virgule et conversion en float 
    	return Number(before+'.'+after);
    }
    Number.prototype.x = function(n){
    	return multi(this,n);
    };
    utilisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    document.write(multi("10*0.1*1.234*1.196"));
    // document.write(multi(10,0.1,1.234,1.196)); // appel sans string
    // document.write((10).x(0.1).x(1.234).x(1.196)); // appel via prototype .x
    document.write('<br/>');
    document.write(10*0.1*1.234*1.196); // résultat erroné
    si vous êtes méga fan de ma solution, je vous autorise à la liker !

    ----------------

    et pour nos amis :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    montantHT = 60;
    montantTTC = Math.floor(multi("montantHT*1.196*100")).x(0.01);
    document.write(montantTTC); // 71.76
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ht = 10;
    tva = 1.18;
    ttc = multi("ht*tva");
    document.write(ttc); // 11.8
    nb: si vous passez l'argument au format "string" contenant les noms de vos variables, vérifiez que leurs "scopes" s'étendent bien au "range" de la fonction "multi". autrement préférez l'écriture "multi(var1,var2)" à "multi('var1*var2')".

  15. #35
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 075
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 075
    Points : 44 657
    Points
    44 657
    Par défaut
    allez maintenant la division
    il restera l'addition, et donc la soustraction, qui n'est pas de chez top non plus
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var i, k = 0;
    for( i = 1; i < 10; i++){
     k += 0.1;
     document.write( k, '<br>');
    }

  16. #36
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    addition et soustraction regroupés sous une seule fonction "add". (ensuite je m'occupe de la division et d'une fonction "calcul" qui prendra un string avec tous les opérateurs et effectuera les bons calcul dans le bon ordre en fonction du poid des opérateurs.

    add :
    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
    37
    38
    39
    function add(n1,n2){
    	if(typeof n1 === 'string') {
    		// découpe des valeurs du string à chaque *
     
    		var arg = n1.replace('-','+-').split('+');
    		// conversion des "number" et vars en number
    		for(var i=0;i<arg.length;i++)
    			arg[i] = eval(arg[i]); 
    		// appel récursif avec des valeurs entières dans chaque paramètre
    		return add.apply(this,arg);
    	}
    	// appel récursif si plus de 2 paramètres
    	if(arguments.length>2)
    		n2 = add.apply(this,[].slice.call(arguments,1)); 
    	// var temporaire
    	var tmp;
    	// nombre de chiffres derrière les virgules
    	var len1 = (tmp = n1.toString().split('.')[1])?tmp.length:0; 
    	var len2 = (tmp = n2.toString().split('.')[1])?tmp.length:0; 
    	// total de la longueur des virgules
    	var len = Math.max(len1,len2);
    	// suppression des virgules
    	var int_n1 = parseInt(n1.toString().replace('.',''),10)*Math.pow(10,len-len1);
    	var int_n2 = parseInt(n2.toString().replace('.',''),10)*Math.pow(10,len-len2);
    	// ajout de "000" devant le result pour prévenir des valeurs inférieures à 0
    	var zero = "0";
    	for(var i=1;i<len;i*=2)
    		zero += zero;
    	// produit sans virgules (format:string)
    	var result =  zero+(int_n1+int_n2 ).toString();
    	// divsion du result en 2 parties : devant et derrière la virgule
    	var before = result.substr(0,result.length-len); 
    	var after = result.substr(result.length-len,result.length); 
    	// rajout de la virgule et conversion en float 
    	return Number(before+'.'+after);
    }
    Number.prototype.add = function(n){
    	return add(this,n);
    };
    exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    document.write(add("10+0.1+1.234-1.196"));

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var i, k = 0;
    for( i = 1; i < 10; i++){
     k = k.add(0.1);
     document.write( k, '<br>');
    }

  17. #37
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    Citation Envoyé par NoSmoking Voir le message
    allez maintenant la division
    il restera l'addition, et donc la soustraction, qui n'est pas de chez top non plus
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var i, k = 0;
    for( i = 1; i < 10; i++){
     k += 0.1;
     document.write( k, '<br>');
    }
    avant que je ne code qqes lignes inutilement, es-tu sûr qu'il y a des erreurs dues à la division flottante ?

  18. #38
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    nb: mes solutions proposées ne fonctionnent qu'avec des flottant suffisaments "grands" ( supérieur à 10^-7) car dans le cas de valeurs inférieures, (0.00000001).toString() me renvoit des truc du genre "1e-7" au lieu de "0.00000001".

  19. #39
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 075
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 075
    Points : 44 657
    Points
    44 657
    Par défaut
    Citation Envoyé par Willpower Voir le message
    avant que je ne code qqes lignes inutilement, es-tu sûr qu'il y a des erreurs dues à la division flottante ?
    pas d'exemple flagrant, la différence ce jouant à la 14éme décimale
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var val1 = 125;
    var val2 = .95;
    CALCULATRICE -> 131.57894736842105263157894736842
    EXCEL        -> 131.578947368421000
    NAVIGATEUR   -> 131.57894736842107
    ma fonction  -> 131.57894736842104

  20. #40
    Membre expérimenté Avatar de Willpower
    Homme Profil pro
    sans emploi
    Inscrit en
    Décembre 2010
    Messages
    1 009
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : sans emploi

    Informations forums :
    Inscription : Décembre 2010
    Messages : 1 009
    Points : 1 519
    Points
    1 519
    Par défaut
    ok, on verra pour les division plus tard alors !

    en attendant, je vous ai créé la magnifique fonction "calc" (basée sur mes fonctions "multi" et "add") qui gère l'addition/soustration, multiplication et gestion des parenthèses pour une calcul donné (passé sous forme de string) sans aucune erreur de virgule flottante :

    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
    37
    38
    39
    40
    41
     
    function add(n1,n2){
    	var tmp,len1,len2,len = Math.max(len1=((tmp = n1.toString().split('.')[1])?tmp.length:0),len2=((tmp = n2.toString().split('.')[1])?tmp.length:0)),zero = "0"
    	for(var i=1;i<len;i*=2)	zero += zero;
    	var result = (parseInt(n1.toString().replace('.',''),10)*Math.pow(10,len-len1)+parseInt(n2.toString().replace('.',''),10)*Math.pow(10,len-len2)).toString();
    	result = (Number(result)<0)?('-'+zero+result.substring(1,result.length)):(zero+result);
    	return Number(result.substr(0,result.length-len)+'.'+result.substr(result.length-len,result.length));
    }
    function multi(n1,n2){
    	var tmp,len = ((tmp = n1.toString().split('.')[1])?tmp.length:0) + ((tmp = n2.toString().split('.')[1])?tmp.length:0),zero = "0";
    	for(var i=1;i<=len;i*=2) zero += zero;
    	var result =  zero+(parseInt(n1.toString().replace('.',''),10)*parseInt(n2.toString().replace('.',''),10)).toString();
    	result = Number(result)<0?'-'+zero+result.substring(1,result.length):zero+result;
    	return Number(result.substr(0,result.length-len)+'.'+result.substr(result.length-len,result.length));
    }
    function calc(expression){
    	// gestion de parenthèses
    	var old_exp = 'Author: Dessy Boris';
    	while(old_exp != expression){
    		old_exp = expression;
    		expression = expression.replace(/\([^\(\)]*\)/,function(a){return calc(a.slice(1,a.length-1));});
    	}
    	// addition/soustraction
    	var addition = expression.replace('-','+-').split('+');
    	if(expression[0]=='-')	addition.shift();
    	if(addition.length>1){
    		expression = 0;
    		for(var i=0;i<addition.length;i++)
    			expression = add(expression,calc(addition[i])); 
    		expression = expression.toString();
    	}
    	// multiplication
    	var multiplication = expression.split('*');
    	if(multiplication.length>1){
    		expression = 1;
    		for(var i=0;i<multiplication.length;i++)
    			expression = multi(expression,calc(multiplication[i])); 
    	}
    	// division et cast du string en number
    	return eval(expression);
    }
    exemples:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    document.write( calc("(2+(0.2*0.2))*(0.2/1)"), '<br>'); // 0.408
    document.write( calc("2+0.2*0.2/3"), '<br>'); // 2.013333333333333
    document.write( calc("60*1.196"), '<br>'); // 71.76
    document.write( calc("10*1.18"), '<br>'); // 11.8
    document.write( calc("-0.2-0.05"), '<br>'); // -0.25
    avec passage de variable globale
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    var tier = 1/3;
    document.write( calc("tier*3"), '<br>'); // 1
    avec passage de variable local (hors scope/range)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    // méthode classique -> fonctionne mal
    for(var i = 0.1; i < 1; i+=0.1){
    	document.write( i, '<br>');
    }
    // avec fonction calc -> fonctionne bien
    for(var i = 0.1; i < 1; i=calc(i+"+0.1")){
    	document.write( i, '<br>');
    }
    priorités :
    -parenthèses
    -division
    -multiplication
    -addition/soustraction

    ps: je viens de l'écrire donc elle comporte sans doute/peut-être des bugs.

    edit:
    bug des parenthèses imbriquées -> corrigé !
    bug des valeurs négatives : corrigé !

Discussions similaires

  1. smartforms affichage de montants avec signe "-"
    Par kitty2006 dans le forum SAP
    Réponses: 6
    Dernier message: 11/12/2007, 13h55
  2. Problème avec RAID5 sur carte Asus M2N32-SLI Deluxe
    Par Dioxine dans le forum Composants
    Réponses: 8
    Dernier message: 17/08/2007, 08h30
  3. Réponses: 5
    Dernier message: 19/04/2007, 12h45
  4. Capture de fronts montants avec un PIC16F876
    Par sylvain42 dans le forum C
    Réponses: 3
    Dernier message: 12/06/2006, 14h48

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