web-dev-qa-db-fra.com

JavaScript% (modulo) donne un résultat négatif pour les nombres négatifs

Selon Google Calculator(-13) % 64 est 51.

Selon Javascript (voir ceci JSBin ), il s'agit de -13.

Comment puis-je réparer ça?

202
Alec Gorge
Number.prototype.mod = function(n) {
    return ((this%n)+n)%n;
};

Extrait de cet article: Le bogue JavaScript Modulo

216
Enrique

Utiliser Number.prototype est SLOW, car chaque fois que vous utilisez la méthode prototype, votre numéro est entouré d'une Object. Au lieu de cela:

Number.prototype.mod = function(n) {
  return ((this % n) + n) % n;
}

Utilisation:

function mod(n, m) {
  return ((n % m) + m) % m;
}

Voir: http://jsperf.com/negative-modulo/2

~ 97% plus rapide que d'utiliser un prototype. Si la performance est importante pour vous, bien sûr.

121
StuR

L'opérateur % en JavaScript est l'opérateur restant, pas l'opérateur modulo (la principale différence réside dans le traitement des nombres négatifs):

-1 % 8 // -1, not 7

24
Rob Sobers

Une fonction "mod" pour retourner un résultat positif.

var mod = function (n, m) {
    var remain = n % m;
    return Math.floor(remain >= 0 ? remain : remain + m);
};
mod(5,22)   // 5
mod(25,22)  // 3
mod(-1,22)  // 21
mod(-2,22)  // 20
mod(0,22)   // 0
mod(-1,22)  // 21
mod(-21,22) // 1

Et bien sur 

mod(-13,64) // 51
14
Shanimal

La réponse acceptée me rend un peu nerveuse car elle réutilise l'opérateur%. Que se passe-t-il si Javascript change le comportement à l'avenir?

Voici une solution de contournement qui ne réutilise pas%:

function mod(a, n) {
    return a - (n * Math.floor(a/n));
}

mod(1,64); // 1
mod(63,64); // 63
mod(64,64); // 0
mod(65,64); // 1
mod(0,64); // 0
mod(-1,64); // 63
mod(-13,64); // 51
mod(-63,64); // 1
mod(-64,64); // 0
mod(-65,64); // 63
8
wisbucky

Opération JavaScript Modulo

La mise en œuvre réussie d'un calcul scientifique ou d'un algorithme est possible non seulement en comprenant les caractéristiques offertes par un langage ou un cadre particulier, mais également en comprenant les limites.

Les ordinateurs sont des instruments scientifiques précis, mais ils fonctionnent en manipulant des entités dans des espaces discrets (vous avez un nombre limité de pixels à l'écran, il y a un nombre limité de bits derrière chaque nombre, etc.)

Essayez d’ignorer les limitations ou les spécifications du framework et vous découvrirez bientôt que vous avez un décalage d’impédance entre votre formule mathématique et le code que vous essayez d’écrire.

Opérateur Modulo

Parfois, la situation est compliquée par des fonctions ou des opérateurs du cadre faussement annoncés ou compris. Cet article se concentre sur l'opérateur modulo.

Demandez à n’importe quel programmeur C # ou JavaScript quel est l’opérateur modulo dans leur langue et il y a de grandes chances pour qu’ils aient disparu:% (par exemple, le signe de pourcentage). Beaucoup de documentation fait référence au signe% en tant qu'opérateur modulo.

Hou la la! C'est une erreur subtile mais très dangereuse. En C # et JavaScript, l'opérateur% est utilisé pour calculer le reste (avec le signe) qui reste lorsqu'un opérande est divisé par le deuxième. Par conséquent, l'opérande doit être correctement appelé opérateur du reste signé.

À première vue, l'opérateur du reste signé fonctionne de la même manière que l'opérateur modulo. Faisons quelques tests en comparant les résultats renvoyés par JavaScript avec ceux renvoyés par Google.

Dans Chrome, ouvrez la console (appuyez sur F12 et sélectionnez l'onglet Console). Tapez là, un par un, les calculs de la colonne de gauche. Tapez ensuite les mêmes expressions dans la barre de recherche Google. Notez les résultats. Ils devraient être les mêmes.

                JavaScript  Google
    5 % 3       2           2
    26 % 26     0           0
    15 % 12     3           3

Essayons maintenant d’utiliser une valeur négative comme premier opérande:

 enter image description here

Surprise!

-5% 3 = 1 (selon Google) - 5% 3 = -2 (selon JavaScript)

Eh bien… cela ne devrait pas être une surprise si nous examinons la définition de% operator en JavaScript (… ou même en C # ou dans d’autres langues). Google calcule le vrai modulo, alors que ces langages informatiques calculent un rappel signé.

Cependant, tous les langages de programmation/cadres ont la même implémentation pour%. En Python, par exemple, l'opérateur% calcule le vrai modulo de la même manière que Google.

 enter image description here

Cette différence de comportement entre les langues peut introduire des erreurs subtiles dans votre calcul, surtout si vous essayez de transférer un algorithme d'une langue à une autre!

Un problème compris est un problème à moitié résolu

Supposons que nous ayons besoin de mettre en œuvre un calcul (scientifique) en JavaScript en utilisant l’arithmétique modulo.

Puisque nous comprenons maintenant que JavaScript n’a pas un véritable opérateur modulo, nous pouvons facilement implémenter notre opération modulo en tant que fonction.

Il y a plusieurs façons d'implémenter modulo en JavaScript. Je vais vous montrer 3 façons de le faire.

// Implement modulo by replacing the negative operand 
// with an equivalent positive operand that has the same wrap-around effect
function mod(n, p)
{
    if ( n < 0 )
        n = p - Math.abs(n) % p;

    return n % p;
}

// Implement modulo by relying on the fact that the negative remainder
// is always p numbers away from a positive reminder
// Ex: -5 % 3 | -5 = -2 * 3 + 1 and -5 = -1 * 3 + (-2) | -2 + 3 = 1  
function mod(n, p)
{
    var r = n % p;

    return r < 0 ? r + p : r;
}

// Implement modulo by solving n = v * p + r equation  
function mod(n, p) 
{
    return n - p * Math.floor( n / p );
}

Avec des outils plus précis à notre disposition, nous sommes maintenant prêts à nous attaquer à ce calcul (scientifique) et à espérer obtenir des résultats corrects à chaque fois.

Remarque: de nombreux calculs utilisent l'arithmétique modulo… Si vous voulez voir comment utiliser ces nouvelles fonctions modulo dans la mise en œuvre d'un code Caesar Cipher/ROT13, vous pouvez vérifier ceci article .

7
Marian Veteanu

Si x est un entier et n est une puissance de 2, vous pouvez utiliser x & (n - 1) au lieu de x % n.

> -13 & (64 - 1)
51 
3
quasimodo

Bien que le comportement ne soit pas conforme à vos attentes, cela ne signifie pas que JavaScript ne se "comporte pas". C'est un choix que JavaScript a fait pour son calcul modulo. Parce que, par définition, l'une ou l'autre réponse a du sens.

Voir this de Wikipedia. Vous pouvez voir à droite comment différentes langues ont choisi le signe du résultat.

3
dheerosaur

Il semble donc que si vous essayez de modifier les degrés (de sorte que si vous avez -50 degrés - 200 degrés), vous voudriez utiliser quelque chose comme:

function modrad(m) {
    return ((((180+m) % 360) + 360) % 360)-180;
}
2
JayCrossler

Je traite aussi avec a négatif et n négatif

 //best perf, hard to read
   function modul3(a,n){
        r = a/n | 0 ;
        if(a < 0){ 
            r += n < 0 ? 1 : -1
        }
        return a - n * r 
    }
    // shorter code
    function modul(a,n){
        return  a%n + (a < 0 && Math.abs(n)); 
    }

    //beetween perf and small code
    function modul(a,n){
        return a - n * Math[n > 0 ? 'floor' : 'ceil'](a/n); 
    }
1
bormat

Ce n'est pas un bug, il y a 3 fonctions pour calculer modulo, vous pouvez utiliser celle qui correspond à vos besoins (je recommanderais d'utiliser la fonction euclidienne)

Tronquer la fonction décimale

console.log(  41 %  7 ); //  6
console.log( -41 %  7 ); // -6
console.log( -41 % -7 ); // -6
console.log(  41 % -7 ); //  6

Fonction partie entière

Number.prototype.mod = function(n) {
    return ((this%n)+n)%n;
};

console.log( parseInt( 41).mod( 7) ); //  6
console.log( parseInt(-41).mod( 7) ); //  1
console.log( parseInt(-41).mod(-7) ); // -6
console.log( parseInt( 41).mod(-7) ); // -1

Fonction euclidienne

Number.prototype.mod = function(n) {
    var m = ((this%n)+n)%n;
    return m < 0 ? m + Math.abs(n) : m;
};

console.log( parseInt( 41).mod( 7) ); // 6
console.log( parseInt(-41).mod( 7) ); // 1
console.log( parseInt(-41).mod(-7) ); // 1
console.log( parseInt( 41).mod(-7) ); // 6
1
zessx

Il existe un package NPM qui fera le travail pour vous. Vous pouvez l'installer avec la commande suivante.

npm install just-modulo --save

Utilisation copiée du fichier README

import modulo from 'just-modulo';

modulo(7, 5); // 2
modulo(17, 23); // 17
modulo(16.2, 3.8); // 17
modulo(5.8, 3.4); //2.4
modulo(4, 0); // 4
modulo(-7, 5); // 3
modulo(-2, 15); // 13
modulo(-5.8, 3.4); // 1
modulo(12, -1); // NaN
modulo(-3, -8); // NaN
modulo(12, 'Apple'); // NaN
modulo('bee', 9); // NaN
modulo(null, undefined); // NaN

Le référentiel GitHub peut être trouvé via le lien suivant:

https://github.com/angus-c/just/tree/master/packages/number-modulo

0
maartenpaauw