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?


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


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 ..


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


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

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

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.


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 

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;
}

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); 
    }

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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.