Pourquoi l'opérateur de module renvoie-t-il le nombre fractionnel en javascript?

pourquoi 49.90 % 0.10 en JavaScript renvoie-t-il 0.09999999999999581 ? Je m'attendais à être 0.

26
demandé sur progyammer 2010-10-19 12:26:04

7 réponses

parce que JavaScript utilise des mathématiques à virgule flottante qui conduisent toujours à des erreurs d'arrondi.

si vous avez besoin d'un résultat exact avec deux décimales, multipliez vos nombres par 100 avant l'opération, puis divisez à nouveau par la suite:

var result = ( 4990 % 10 ) / 100;

ronde si nécessaire.

43
répondu Aaron Digulla 2010-10-19 08:28:44

le numéro de Javascript utilise "IEEE double-precision" pour stocker les valeurs. Ils sont incapables de stocker tous les nombres décimaux exactement. Le résultat n'est pas zéro à cause d'une erreur d'arrondi lors de la conversion du nombre décimal en nombre binaire.

49.90 = 49.89999999999999857891452848...
 0.10 =  0.10000000000000000555111512...

Ainsi plancher(49.90 / 0.10) est seulement 498, et le reste sera 0.09999....


il semble que vous utilisez des nombres pour stocker la quantité de dollars. Ne pas faites ce , car les opérations en virgule flottante se propagent et amplifient l'erreur d'arrondi. Conservez le nombre comme montant de cents à la place. Entier peut être représenté exactement, et 4990 % 10 retournera 0.

17
répondu kennytm 2017-05-23 10:30:45

je vais juste laisser ceci ici pour référence future, mais voici une fonction pratique qui peut gérer plus précisément reste (depuis JS n'a pas d'opérateur modulo ) impliquant des flotteurs.

  function floatSafeRemainder(val, step){
    var valDecCount = (val.toString().split('.')[1] || '').length;
    var stepDecCount = (step.toString().split('.')[1] || '').length;
    var decCount = valDecCount > stepDecCount? valDecCount : stepDecCount;
    var valInt = parseInt(val.toFixed(decCount).replace('.',''));
    var stepInt = parseInt(step.toFixed(decCount).replace('.',''));
    return (valInt % stepInt) / Math.pow(10, decCount);
  }

$(function() {
  
  
  function floatSafeModulus(val, step) {
    var valDecCount = (val.toString().split('.')[1] || '').length;
    var stepDecCount = (step.toString().split('.')[1] || '').length;
    var decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
    var valInt = parseInt(val.toFixed(decCount).replace('.', ''));
    var stepInt = parseInt(step.toFixed(decCount).replace('.', ''));
    return (valInt % stepInt) / Math.pow(10, decCount);
  }
  
  
  $("#form").submit(function(e) {
    e.preventDefault();
    var safe = 'Invalid';
    var normal = 'Invalid';
    var var1 = parseFloat($('#var1').val());
    var var2 = parseFloat($('#var2').val());
    if (!isNaN(var1) && !isNaN(var2)) {
      safe = floatSafeModulus(var1, var2);
      normal = var1 % var2
    }
    $('#safeResult').text(safe);
    $('#normalResult').text(normal);
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="form" novalidate>
  <div>
    <input type="number" id="var1">%
    <input type="number" id="var2">
  </div>
  <div>safe: <span id="safeResult"></span><div>
  <div>normal (%): <span id="normalResult"></span></div>
  <input type="submit" value="try it out">
</form>
8
répondu TheSharpieOne 2015-07-29 21:00:35

http://en.wikipedia.org/wiki/Modulo_operation Ne soyez pas en colère modulo est utilisé avec des entiers ^^ Ainsi des valeurs flottantes se produisent quelques erreurs.

1
répondu MatTheCat 2010-10-19 08:29:44

regardez floating points et ses inconvénients - un nombre comme 0.1 ne peut pas être enregistré correctement comme floating point, il y aura toujours de tels problèmes. Prenez vos nombres *10 ou *100 et faites les calculs avec des entiers à la place.

1
répondu oezi 2014-11-17 10:01:58

Cause

Floating point ne peut pas stocker toutes les valeurs décimales exactement. Ainsi, lors de l'utilisation des formats à virgule flottante, il y aura toujours des erreurs d'arrondissement sur les valeurs d'entrée. Les erreurs sur les entrées résultent bien sûr sur les erreurs sur la sortie. Dans le cas d'une fonction ou d'un opérateur discret, il peut y avoir une grande différence sur la sortie autour du point où la fonction ou l'opérateur est discret. L'opérateur modula est discret et votre cas est clairement exemple de ce problème.

d'Entrée et de sortie pour les valeurs à virgule flottante

ainsi, lorsque vous utilisez des variables à virgule flottante, vous devriez toujours être conscient de cela. Et quelle que soit la sortie que vous voulez d'un calcul avec des points flottants devrait toujours être formaté/conditionné avant l'affichage avec ceci à l'esprit.

Lorsque seules des fonctions et des opérateurs continus sont utilisés, l'arrondi à la précision désirée faire (ne pas tronquer). Les fonctionnalités de formatage Standard utilisées pour convertir les flotteurs en chaîne le feront généralement pour vous.

Pour avoir une sortie correcte basée sur la précision attendue des entrées et la précision désirée de la sortie, vous devez également

  • des entrées rondes à la précision attendue ou s'assurer qu'aucune valeur ne peut être entrée avec une plus grande précision.
  • ajouter une petite valeur aux sorties avant de les arrondir / formater qui est plus petit ou égal au quart de la précision désirée et plus grand que l'erreur maximale prévue causée par les erreurs d'arrondi à l'entrée et pendant le calcul. Si cela n'est pas possible, la combinaison de la précision du type de données n'est pas suffisant pour fournir le résultat souhaité de précision pour votre calcul.

ces 2 choses ne sont souvent pas faites et dans la plupart des cas, les différences causées par le fait de ne pas les faire sont trop petites pour être importantes pour la plupart des utilisateurs, mais je déjà eu un projet où la sortie n'a pas été acceptée par les utilisateurs sans ces corrections.

Discrète des fonctions ou opérateurs (comme modula)

lorsque des opérateurs ou des fonctions discrets sont impliqués, des corrections supplémentaires peuvent être nécessaires pour s'assurer que la sortie est comme prévu. Arrondir et ajouter de petites corrections avant d'arrondir ne peut pas résoudre le problème.

Un contrôle/correction spécial sur l'intermédiaire les résultats du calcul, immédiatement après l'application de la fonction discrète ou l'exploitant peut être nécessaire.

cas spécifique de cette question

dans ce cas, vous attendez l'entrée avec une certaine précision, il est donc possible de corriger la sortie pour l'impact des erreurs d'arrondissement qui sont beaucoup plus petites que la précision désirée.

si nous disons que la précision de votre type de données est E.

Votre l'entrée ne sera pas être enregistrées en tant que valeurs de a et b que vous avez entré, mais a*(1+/-e) et b*(1+/e -)

Le résultat d'une division a*(1+/-e) par b*(1+/-e) (a/b) (1+/-2e).

La fonction modula doit tronquer le résultat et se multiplier à nouveau. Donc le résultat sera(a/b
b) (1+/-3e) = a (1+ / -3e) résultant en une erreur de a*3e.

Le mod ajoute un*e de l'erreur d'un*3e en raison de la soustraction de 2 valeurs avec des erreurs possibles de a*3e et a*e.

Vous devez donc vérifier que l'erreur totale possible a*4e est plus petite que la précision désirée et si cette condition est respectée et que le résultat ne diffère pas plus de b que cette erreur maximale possible, vous pouvez la remplacer en toute sécurité par 0.

mieux éviter d'avoir le problème

il est souvent plus efficace d'éviter ces problèmes en utilisant des données types (formats entier ou point fixe) pour des calculs comme celui-ci qui peuvent stocker l'entrée attendue sans erreurs d'arrondissement. Par exemple, vous ne devriez jamais utiliser les valeurs à virgule flottante pour les calculs financiers.

1
répondu Stefan Mondelaers 2017-07-27 13:20:22

Ce n'est pas une réponse parfaite, mais il fonctionne.

function format_float_bug(num)
{
   return parseFloat( num.toFixed(15) ); 
} 

vous pouvez utiliser comme suit,

format_float_bug(4990 % 10);

parce qu'en dessous du nombre (49.89999999999999857891452848) les premières 15 décimales sont comme 9999999

-3
répondu Viranga 2014-02-17 11:16:23