JavaScript% (Modulo) liefert ein negatives Ergebnis für negative Zahlen

252

Laut Google Calculator (-13) % 64 ist 51.

Laut Javascript (siehe diese JSBin ) ist es -13.

Wie behebe ich das?

Alec-Schlucht
quelle
Dies kann nur ein Vorrang sein. Meinst du (-13) % 64oder -(13 % 64)? Persönlich würde ich die Parens so oder so einsetzen, nur um zusätzliche Klarheit zu schaffen.
MatrixFrog
2
Im Wesentlichen ein Duplikat von Wie führt Java Modulberechnungen mit negativen Zahlen durch? obwohl dies eine Javascript-Frage ist.
Präsident James K. Polk
85
Javascript fühlt sich manchmal wie ein sehr grausamer Witz an
dukeofgaming
6
Google kann nicht falsch sein
Caub
10
Das grundlegende Problem ist, dass JS %nicht der Modulo-Operator ist. Es ist der Restoperator. In JavaScript gibt es keinen Modulo-Operator. Die akzeptierte Antwort ist also der richtige Weg.
Reduzieren Sie

Antworten:

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

Entnommen aus diesem Artikel: Der JavaScript Modulo Bug

Enrique
quelle
23
Ich weiß nicht, dass ich es einen "Bug" nennen würde. Die Modulo-Operation ist über negative Zahlen nicht sehr gut definiert, und verschiedene Computerumgebungen behandeln sie unterschiedlich. Der Wikipedia-Artikel über die Modulo-Operation behandelt dies ziemlich gut.
Daniel Pryden
22
Es mag dumm erscheinen, da es oft als "Modulo" bezeichnet wird, was darauf hindeutet, dass es sich genauso verhält wie seine mathematische Definition (siehe ℤ / nℤ-Algebra), was es nicht tut.
Etienne
7
Warum das Modulo nehmen, bevor Sie n hinzufügen? Warum nicht einfach n hinzufügen und dann das Modulo nehmen?
Starwed
12
@starwed Wenn Sie dieses% n nicht verwenden würden, würde es fehlschlagen x < -n- zB (-7 + 5) % 5 === -2aber ((-7 % 5) + 5) % 5 == 3.
Fadedbee
7
Ich empfehle, der Antwort hinzuzufügen, dass für den Zugriff auf diese Funktion das Format (-13) .mod (10) anstelle von -13% 10 verwendet werden sollte. Es wäre klarer.
Jp_
161

Die Verwendung Number.prototypeist LANGSAM, da Ihre Nummer jedes Mal, wenn Sie die Prototypmethode verwenden, in eine eingeschlossen wird Object. An Stelle von:

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

Verwenden:

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

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

~ 97% schneller als bei Verwendung eines Prototyps. Wenn Leistung für Sie natürlich wichtig ist ..

StuR
quelle
1
Toller Tipp. Ich nahm Ihren jsperf und verglich ihn mit den anderen Lösungen in dieser Frage (aber es scheint, dass dies sowieso der beste ist): jsperf.com/negative-modulo/3
Mariano Desanze
11
Mikrooptimierung. Sie müßten eine zu tun , massiv für diese Menge an mod Berechnungen keinen Unterschied überhaupt zu machen. Codieren Sie, was am klarsten und am wartbarsten ist, und optimieren Sie anschließend die Leistungsanalyse.
ChrisV
Ich denke, Sie haben Ihre ns und ms in Ihrem zweiten Beispiel @StuR falsch herum. Es sollte sein return ((n % m) + m) % m;.
Vimist
Dies sollte ein Kommentar zur akzeptierten Antwort sein, keine Antwort für sich.
Xehpuk
5
Die in dieser Antwort angegebene Motivation ist eine Mikrooptimierung, ja, aber das Modifizieren des Prototyps ist problematisch. Bevorzugen Sie den Ansatz mit den wenigsten Nebenwirkungen, nämlich diesen.
Scharf
30

Der %Operator in JavaScript ist der Restoperator, nicht der Modulo-Operator (der Hauptunterschied besteht darin, wie negative Zahlen behandelt werden):

-1 % 8 // -1, not 7

Rob Sobers
quelle
8
Es sollte den Rest Operator genannt werden , aber es wird Modulus - Operator genannt: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/...
Big McLargeHuge
15
@ DaveKennedy: MDN ist keine offizielle Sprachreferenz, sondern eine von der Community bearbeitete Site, die manchmal etwas falsch macht. Die Spezifikation nennt es keinen Modulo-Operator, und soweit ich das beurteilen kann, hat sie es nie getan (ich bin zu ES3 zurückgekehrt). Es heißt ausdrücklich, dass der Operator den Rest einer impliziten Division ergibt und nennt ihn einfach "den% -Operator".
TJ Crowder
2
Wenn es aufgerufen wird remainder, muss es per Definition größer als 0 sein. Kannst du dich nicht an den Teilungssatz von der High School erinnern ?! Vielleicht können Sie hier einen Blick darauf werfen: en.wikipedia.org/wiki/Euclidean_division
Ahmad
19

Eine "Mod" -Funktion, um ein positives Ergebnis zurückzugeben.

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

Und natürlich

mod(-13,64) // 51
Shanimal
quelle
1
MDN ist keine offizielle Sprachreferenz, sondern eine von der Community bearbeitete Site, die manchmal etwas falsch macht. Die Spezifikation nennt es keinen Modulo-Operator, und soweit ich das beurteilen kann, hat sie es nie getan (ich bin zu ES3 zurückgekehrt). Es heißt ausdrücklich, dass der Operator den Rest einer impliziten Division ergibt und nennt ihn einfach "den% -Operator".
TJ Crowder
1
Hoppla, der von Ihnen angegebene Link verweist tatsächlich #sec-applying-the-mod-operatorgenau dort in der URL :) Wie auch immer, danke für den Hinweis, ich habe den Flaum aus meiner Antwort genommen, es ist sowieso nicht wirklich wichtig.
Shanimal
3
@ Shanimal: LOL! Es tut. Ein Fehler des HTML-Editors. Der Spezifikationstext nicht.
TJ Crowder
10

Die akzeptierte Antwort macht mich etwas nervös, weil sie den% -Operator wiederverwendet. Was ist, wenn Javascript das Verhalten in Zukunft ändert?

Hier ist eine Problemumgehung, bei der% nicht wiederverwendet wird:

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
wisbucky
quelle
8
Wenn Javascript den Modulo-Operator so ändern würde, dass er mit der mathematischen Definition übereinstimmt, würde die akzeptierte Antwort weiterhin funktionieren.
Starwed
20
"Was ist, wenn Javascript das Verhalten in Zukunft ändert?" - Warum sollte es? Eine Änderung des Verhaltens eines solchen grundlegenden Operators ist unwahrscheinlich.
nnnnnn
1
+1 für das Teilen dieses Anliegens von & Alternative zu der vorgestellten Antwort # answer-4467559 & aus 4 Gründen: (1) Warum es heißt, & ja "Eine Änderung des Verhaltens einer solchen grundlegenden Operation ist nicht wahrscheinlich", aber dennoch umsichtig zu prüfen selbst um herauszufinden, dass es nicht benötigt wird. (2) Das Definieren einer Arbeitsoperation in Bezug auf eine kaputte ist zwar beeindruckend, aber zumindest auf den ersten Blick besorgniserregend. Es sollte so lange nicht gezeigt werden, bis ich diese Alternative nicht gut verifiziert habe. Ich finde es einfacher, sie weiterzuverfolgen schneller Blick. (4) winzig: Es verwendet 1 Div + 1 Mul anstelle von 2 (Mod) Divs und ich habe auf VIEL früherer Hardware ohne eine gute FPU gehört, dass die Multiplikation schneller war.
Destiny Architect
2
@DestinyArchitect es ist nicht umsichtig, es ist sinnlos. Wenn sie das Verhalten des Restoperators ändern würden, würde dies eine Reihe von Programmen beschädigen, die ihn verwenden. Das wird niemals passieren.
Aegis
10
Was passiert , wenn das Verhalten -, *, /, ;, ., (, ), ,, Math.floor, functionoder returnÄnderungen? Dann ist dein Code schrecklich kaputt.
Xehpuk
5

Obwohl es sich nicht wie erwartet verhält, bedeutet dies nicht, dass sich JavaScript nicht "verhält". Es ist ein JavaScript, das für die Modulo-Berechnung ausgewählt wurde. Denn per Definition macht jede Antwort Sinn.

Siehe dies aus Wikipedia. Sie können rechts sehen, wie verschiedene Sprachen das Vorzeichen des Ergebnisses ausgewählt haben.

Dheerosaurier
quelle
4

Wenn xes sich um eine Ganzzahl und eine Zweierpotenz nhandelt, können Sie x & (n - 1)anstelle von verwenden x % n.

> -13 & (64 - 1)
51 
Quasimodo
quelle
2

Wenn Sie also versuchen, um Grad zu modifizieren (wenn Sie also -50 Grad - 200 Grad haben), möchten Sie Folgendes verwenden:

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

Ich beschäftige mich auch mit negativem a und negativem n

 //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); 
    }
Bormat
quelle
1

Dies ist kein Fehler, es gibt 3 Funktionen zur Berechnung von Modulo. Sie können diejenige verwenden, die Ihren Anforderungen entspricht (ich würde empfehlen, die euklidische Funktion zu verwenden).

Abschneiden der Dezimalteilfunktion

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

Ganzzahlige Teilfunktion

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

Euklidische Funktion

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
Zessx
quelle
1
In der euklidischen Funktion ist die Überprüfung von m <0 nutzlos, weil ((dieses% n) + n)% n immer positiv ist
bormat
1
@bormat Ja, das ist es, aber in Javascript %kann negative Ergebnisse zurückgeben (und dies ist der Zweck dieser Funktionen, um es zu beheben)
zessx
Sie haben diesen [Code] geschrieben Number.prototype.mod = function (n) {var m = ((this% n) + n)% n; return m <0? m + Math.abs (n): m; }; [/ code] gib mir einen Wert von n, wobei m négativ ist. Sie sind kein Wert von n, wobei m negativ ist, weil Sie n nach dem ersten% hinzufügen.
Bormat
Ohne diese Prüfung parseInt(-41).mod(-7)würde -6statt 1(und das ist genau der Zweck der Integer-Teil-Funktion, die ich geschrieben habe)
zessx
1
Sie können Ihre Funktion vereinfachen, indem Sie das zweite Modulo Number.prototype.mod = function (n) {var m = this% n; zurück (m <0)? m + Math.abs (n): m; };
Bormat
0

Es gibt ein NPM-Paket, das die Arbeit für Sie erledigt. Sie können es mit dem folgenden Befehl installieren.

npm install just-modulo --save

Verwendung aus der README-Datei kopiert

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

Das GitHub-Repository finden Sie über den folgenden Link:

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

maartenpaauw
quelle