Ganzzahlige Teilung mit Rest in JavaScript?

Antworten:

1239

Berechnen Sie für eine Zahl yund einen Teiler xden Quotienten ( quotient) und den Rest ( remainder) wie folgt :

var quotient = Math.floor(y/x);
var remainder = y % x;
Mark Elliot
quelle
84
% arbeitet mit Floats in JavaScript (dies unterscheidet sich von vielen anderen Sprachen), was möglicherweise nicht erwünscht ist: ergibt 3.5 % 21.5. Stellen Sie sicher, dass Sie (parseInt, floor usw.) wie erforderlich behandeln
16
Der Integralteil von -4,5 in der Mathematik ist -5, da -5 die "höchstmögliche Integralzahl ist, die immer noch niedriger als -4,5 ist".
Toughy
9
Was auch immer Sie sich für negative Zahlen entscheiden, es sollte über den Quotienten und den Rest hinweg konsistent sein. Die Verwendung von floorund %zusammen ist auf diese Weise nicht konsistent. Verwenden Sie entweder truncanstelle von floor(wodurch negative Reste zugelassen werden) oder verwenden Sie die Subtraktion, um den Rest zu erhalten ( rem = y - div * x).
Mark Reed
7
1. Wenn Sie den Rest remtrotzdem berechnen möchten, können Sie den Quotienten divschneller ohne Bodenbelag erhalten : (y - rem) / x. 2. Übrigens ist die Modulo-Operation nach Donald Knuths empfohlener Definition (Vorzeichen-Übereinstimmungs-Divisor, nicht der Rest, dh der euklidische Modul, noch die JavaScript-Vorzeichen-Übereinstimmungs-Dividende) das, was wir in JavaScript codieren können function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }.
Aaron Mansheim
1
-9 / 2 = -4,5. Sie nehmen dann das Wort von -4,5, was -5 ist. Denken Sie daran, dass -5 kleiner als -4,5 ist und die Bodenoperation als die größte Ganzzahl definiert ist, die kleiner als ein bestimmter Wert ist.
Mark Reed
371

Ich bin kein Experte für bitweise Operatoren, aber hier ist eine andere Möglichkeit, die ganze Zahl zu erhalten:

var num = ~~(a / b);

Dies funktioniert auch bei negativen Zahlen einwandfrei, während Math.floor()in die falsche Richtung gerundet wird.

Dies scheint auch richtig zu sein:

var num = (a / b) >> 0;
user113716
quelle
84
Ein anderer, dessen Zweck ich gerade die letzten 20 Minuten damit verbracht habe, herauszufinden, ist anscheinenda/b | 0
BlueRaja - Danny Pflughoeft
18
@ user113716 @BlueRaja Bitweise Operationen sind nur für Integer-Typen sinnvoll und JS weiß das (natürlich). ~~int, int | 0Und int >> 0ändert nicht anfängliches Argument, sondern Dolmetscher integralen Bestandteils an Betreibern Pass.
Aleksei Zabrodskii
15
floordreht sich angesichts seines Namens kaum in die falsche Richtung - nur nicht in die Richtung, die die Leute im Allgemeinen wollen!
Mark K Cowan
19
Das ist ein Buu Buu. a = 12447132275286670000; b = 128 Math.floor(a/b)-> 97243220900677100und ~~(a/b)-> -1231452688.
Mirek Rusin
7
Seien Sie vorsichtig mit Vorrang. ~~(5/2) --> 2wie auch (5/2)>>0 --> 2, aber ~~(5/2) + 1 --> 3während ~~(5/2)>>0 + 1 --> 1. ~~ist eine gute Wahl, da der Vorrang angemessener ist.
Timkay
217

Ich habe einige Geschwindigkeitstests mit Firefox durchgeführt.

-100/3             // -33.33..., 0.3663 millisec
Math.floor(-100/3) // -34,       0.5016 millisec
~~(-100/3)         // -33,       0.3619 millisec
(-100/3>>0)        // -33,       0.3632 millisec
(-100/3|0)         // -33,       0.3856 millisec
(-100-(-100%3))/3  // -33,       0.3591 millisec

/* a=-100, b=3 */
a/b                // -33.33..., 0.4863 millisec
Math.floor(a/b)    // -34,       0.6019 millisec
~~(a/b)            // -33,       0.5148 millisec
(a/b>>0)           // -33,       0.5048 millisec
(a/b|0)            // -33,       0.5078 millisec
(a-(a%b))/b        // -33,       0.6649 millisec

Das Obige basiert auf jeweils 10 Millionen Versuchen.

Schlussfolgerung: Verwenden Sie (a/b>>0)(oder (~~(a/b))oder (a/b|0)), um einen Effizienzgewinn von ca. 20% zu erzielen. Denken Sie auch daran, dass sie alle nicht mit dem Math.floorZeitpunkt übereinstimmen a/b<0 && a%b!=0.

KalEl
quelle
54
Beachten Sie, dass die Optimierung der Ganzzahldivision für die Geschwindigkeit nur dann sinnvoll ist, wenn Sie viel tun . In jedem anderen Fall würde ich empfehlen, die einfachste zu wählen (je nachdem, was Ihnen und Ihren Mitarbeitern am einfachsten erscheint).
Mik01aj
7
@ m01 stimme voll und ganz zu - es gibt viel zu viel Fokus auf
solche Dinge
2
@ m01 Aber was ist schwieriger: Lernen über Math.floorund wer-weiß-wie-viele andere API-Funktionen oder Lernen über den ~(bitweise-nicht) Operator und wie bitweise Operationen in JS funktionieren und dann den Effekt von Double Tilde verstehen ?
Stijn de Witt
11
Wenn Ihre Mitarbeiter keine Chips in Assembler programmieren, werden sie diese wahrscheinlich Math.floorbesser verstehen . Und selbst wenn nicht, ist dieser googleable.
Mik01aj
2
@MarkGreen Ja, aber wenn Sie das nur wollen, heißt das nicht, dass Sie es in einer seltsamen Form schreiben sollten, nur weil es ohne guten Grund am schnellsten ist - Codeklarheit im Allgemeinen sollte normalerweise Ihr erstes Anliegen sein. Außerdem sind diese Tests nach Sprachänderungen und in verschiedenen Browsern möglicherweise völlig bedeutungslos. Sie müssen ein Profil erstellen, um herauszufinden, was in Ihrer Anwendung langsam ist. Es ist unwahrscheinlich, dass dies Ihre ganzzahlige Divisionsmethode ist, es sei denn, Sie haben bereits eine einzelne andere Sache optimiert!
JonnyRaa
150

ES6 führt die neue Math.truncMethode ein. Auf diese Weise kann die Antwort von @ MarkElliot korrigiert werden , damit sie auch für negative Zahlen funktioniert:

var div = Math.trunc(y/x);
var rem = y % x;

Beachten Sie, dass MathMethoden gegenüber bitweisen Operatoren den Vorteil haben, dass sie mit Zahlen über 2 31 arbeiten .

Oriol
quelle
var y = 18014398509481984; var x = 5; div =? - Fehler?
4esn0k
4
@ 4esn0k Es ist kein Fehler. Ihre Nummer hat zu viele Ziffern. In einer IEEE 754-Nummer im 64-Bit-Binärformat können Sie nicht so genau sein. Zum Beispiel 18014398509481984 == 18014398509481985.
Oriol
18014398509481984 == 2 ** 54, und ich habe diese Nummer speziell verwendet, da sie genau im binären 64-Format dargestellt wird. und die Antwort ist auch genau dargestellt
4esn0k
1
Ich denke, die Wahl ist einfach: Sie benötigen Unterstützung für Zahlen mit bis zu 32 Bit Vorzeichen? Verwenden Sie ~~(x/y). Müssen größere Zahlen bis zu 54 Bit signiert unterstützt werden? Verwenden Math.truncSie es, wenn Sie es haben oder auf Math.floorandere Weise (korrekt für negative Zahlen). Müssen Sie noch größere Zahlen unterstützen? Verwenden Sie eine große Zahlenbibliothek.
Stijn de Witt
4
Für Rubyisten hier von Google auf der Suche nach divmodkönnen Sie es als solches implementieren:function divmod(x, y) { var div = Math.trunc(x/y); var rem = x % y; return [div, rem]; }
Alex Moore-Niemi
29
var remainder = x % y;
return (x - remainder) / y;
Gammax
quelle
1
Diese Version besteht den Test leider nicht, wenn x = -100 ist, da sie -34 anstelle von -33 zurückgibt.
Samuel
1
was ist mit "var x = 0,3; var y = 0,01;" ? (danke an github.com/JuliaLang/julia/issues/4156#issuecomment-23324163 )
4esn0k
Tatsächlich, @Samuel, mit negativen Werten, gibt diese Methode korrekte Ergebnisse zurück, oder zumindest die gleichen Werte wie die Methode, die Math.trunc:) verwendet. Ich habe mit 100,3 nachgefragt; -100,3; 100, -3 und -100, -3. Natürlich ist viel Zeit vergangen, seit sich Ihr Kommentar und die Dinge geändert haben.
Marjan Venema
19

Ich benutze normalerweise:

const quotient =  (a - a % b) / b;
const remainder = a % b;

Es ist wahrscheinlich nicht das eleganteste, aber es funktioniert.

Wolf Elkan
quelle
1
Gute Lösung, weil sie die Hässlichkeit des Parsens oder Abschneidens eines Schwimmers vermeidet.
Dem Pilafian
6
Wenn Sie sowohl den Quotienten als auch den Rest benötigen, berechnen Sie zuerst den Rest und verwenden Sie dann diesen Wert im Ausdruck für den Quotienten wieder, dh Quotient = (a - Rest) / b;
GB96
2
Rest = a% b; Quotient = (a - Rest) / b;
Zv_oDD
16

Sie können die Funktion verwenden parseInt, um ein abgeschnittenes Ergebnis zu erhalten.

parseInt(a/b)

Verwenden Sie den Mod-Operator, um einen Rest zu erhalten:

a%b

parseInt hat einige Fallstricke mit Strings, um die Verwendung von Radix-Parametern mit Basis 10 zu vermeiden

parseInt("09", 10)

In einigen Fällen kann die Zeichenfolgendarstellung der Zahl eine wissenschaftliche Notation sein. In diesem Fall führt parseInt zu einem falschen Ergebnis.

parseInt(100000000000000000000000000000000, 10) // 1e+32

Dieser Aufruf erzeugt 1 als Ergebnis.

Édipo Costa Rebouças
quelle
7
parseIntsollte nach Möglichkeit vermieden werden. Hier ist Douglas Crockfords Warnung: "Wenn das erste Zeichen der Zeichenfolge 0 ist, wird die Zeichenfolge in Basis 8 anstelle von Basis 10 ausgewertet. In Basis 8 sind 8 und 9 keine Ziffern, also parseInt (" 08 ") und parseInt ("09") erzeugen 0 als Ergebnis. Dieser Fehler verursacht Probleme in Programmen, die Datums- und Uhrzeitangaben analysieren. Glücklicherweise kann parseInt einen Radix-Parameter verwenden, sodass parseInt ("08", 10) 8 erzeugt. Ich empfehle dies immer Geben Sie den Radix-Parameter an. " archive.oreilly.com/pub/a/javascript/excerpts/…
Powers
3
In einer Division erwarte ich eine Nummer, keine Zeichenfolge, aber das ist ein guter Punkt.
Édipo Costa Rebouças
2
@Powers also füge den Radix hinzu. Er sagt nicht, parseIntdass vermieden werden sollte; nur, dass es einige Fallstricke gibt, die man beachten muss. Sie müssen sich dieser Dinge bewusst sein und bereit sein, damit umzugehen.
Keine
2
Rufen Sie niemals parseIntmit einem Zahlenargument an. parseIntsoll teilweise numerische Zeichenfolgen analysieren, keine Zahlen abschneiden.
Oriol
1
Nur weil die Dinge ursprünglich nicht für eine bestimmte Verwendung gedacht sind, heißt das nicht, dass Sie es nicht sollten. Diese Antwort funktioniert.
Fregante
6

JavaScript berechnet den Boden der negativen Zahlen und den Rest der nicht ganzzahligen Zahlen nach den mathematischen Definitionen für sie.

FLOOR ist definiert als "die größte Ganzzahl, die kleiner als der Parameter ist", also:

  • positive Zahlen: FLOOR (X) = ganzzahliger Teil von X;
  • negative Zahlen: FLOOR (X) = ganzzahliger Teil von X minus 1 (weil er KLEINER sein muss als der Parameter, dh negativer!)

REST bleibt definiert als "Rest" einer Division (euklidische Arithmetik). Wenn die Dividende keine ganze Zahl ist, ist der Quotient normalerweise auch keine ganze Zahl, dh es gibt keinen Rest, aber wenn der Quotient gezwungen ist, eine ganze Zahl zu sein (und das passiert, wenn jemand versucht, den Rest oder den Modul von a zu erhalten Gleitkommazahl), es wird offensichtlich eine Nicht-Ganzzahl "übrig" sein.

JavaScript berechnet alles wie erwartet, daher muss der Programmierer darauf achten, die richtigen Fragen zu stellen (und die Leute sollten darauf achten, die gestellten Fragen zu beantworten!). Yarins erste Frage lautete NICHT "Was ist die ganzzahlige Division von X durch Y", sondern stattdessen "geht die GANZE Häufigkeit, mit der eine bestimmte Ganzzahl in eine andere übergeht". Für positive Zahlen ist die Antwort für beide gleich, jedoch nicht für negative Zahlen, da die ganzzahlige Division (Dividende durch Divisor) -1 kleiner ist als die Zeiten, in denen eine Zahl (Divisor) in eine andere "geht" (Dividende). Mit anderen Worten, FLOOR gibt die richtige Antwort für eine ganzzahlige Division einer negativen Zahl zurück, aber Yarin hat das nicht gefragt!

gammax antwortete richtig, dieser Code funktioniert wie von Yarin gefragt. Auf der anderen Seite liegt Samuel falsch, er hat wohl nicht nachgerechnet, oder er hätte gesehen, dass es funktioniert (außerdem hat er nicht gesagt, was der Teiler seines Beispiels war, aber ich hoffe, es war 3):

Rest = X% Y = -100% 3 = -1

GoesInto = (X - Rest) / Y = (-100 - -1) / 3 = -99 / 3 = -33

Übrigens habe ich den Code unter Firefox 27.0.1 getestet. Er funktionierte wie erwartet, mit positiven und negativen Zahlen sowie mit nicht ganzzahligen Werten, sowohl für Dividenden als auch für Divisoren. Beispiel:

-100,34 / 3,57: GoesInto = -28, Rest = -0,3800000000000079

Ja, ich habe festgestellt, dass es dort ein Präzisionsproblem gibt, aber ich hatte keine Zeit, es zu überprüfen (ich weiß nicht, ob es ein Problem mit Firefox, Windows 7 oder der FPU meiner CPU ist). Für Yarins Frage, die nur ganze Zahlen enthält, funktioniert der Gammax-Code jedoch perfekt.

Cyberknight
quelle
5

Math.floor(operation) Gibt den abgerundeten Wert der Operation zurück.

Beispiel 1 st Frage:

var x = 5;
var y = 10.4;
var z = Math.floor(x + y);

console.log(z);

Konsole:

fünfzehn

Beispiel 2 nd Frage:

var x = 14;
var y = 5;
var z = Math.floor(x%y);

console.log(x);

Konsole:

4

Aetrizität
quelle
2

Die Berechnung der Anzahl der Seiten kann in einem Schritt erfolgen: Math.ceil (x / y)

user1920925
quelle
Ich sehe nicht, wie dies den Rest liefert.
Paul Rooney
1

Alex Moore-Niemis Kommentar als Antwort:

Für Rubyisten hier von Google auf der Suche nach divmodkönnen Sie es als solches implementieren:

function divmod(x, y) {
  var div = Math.trunc(x/y);
  var rem = x % y;
  return [div, rem];
}

Ergebnis:

// [2, 33]
Alex
quelle
2
divmodVerwendet normalerweise Floored Division ( Math.floor), was sich von abgeschnittener Division ( Math.trunc) unterscheidet, wenn negative Zahlen beteiligt sind. Dies gilt auch für das NPM- divmodPaket , Rubydivmod , SWI-Prologdivmod und wahrscheinlich auch für viele andere Implementierungen.
Palec
Eine abgeschnittene Teilung liefert natürlichere Ergebnisse als eine Bodenunterteilung, aber die Kompatibilität übertrifft diese, IMO. Möglicherweise gibt es auch mathematische oder Leistungsgründe für die Verwendung der Bodenteilung. Beachten Sie, dass dies normalerweise der divmodFall ist, da es doppelt so schnell ausgeführt wird wie die getrennte Berechnung der beiden Operationen. Das Bereitstellen einer solchen Funktion ohne diesen Leistungsvorteil kann verwirrend sein.
Palec
1

Wenn Sie nur mit Zweierpotenzen teilen, können Sie bitweise Operatoren verwenden:

export function divideBy2(num) {
  return [num >> 1, num & 1];
}

export function divideBy4(num) {
  return [num >> 2, num & 3];
}

export function divideBy8(num) {
  return [num >> 3, num & 7];
}

(Der erste ist der Quotient, der zweite der Rest)

Konstantin Möllers
quelle
Im Allgemeinen function divideByPowerOf2(num, exponent) { return [num >> exponent, num & ((1 << exponent) - 1)]; }.
Palec
0

Sie können ternär verwenden, um zu entscheiden, wie auch positive und negative ganzzahlige Werte behandelt werden sollen.

var myInt = (y > 0) ? Math.floor(y/x) : Math.floor(y/x) + 1

Wenn die Zahl positiv ist, ist alles in Ordnung. Wenn die Zahl negativ ist, wird 1 hinzugefügt, da Math.floor mit Negativen umgeht.

John Galt
quelle
0

Dies wird immer gegen Null abgeschnitten. Ich bin mir nicht sicher, ob es zu spät ist, aber hier ist es:

function intdiv(dividend, divisor) { 
    divisor = divisor - divisor % 1;
    if (divisor == 0) throw new Error("division by zero");
    dividend = dividend - dividend % 1;
    var rem = dividend % divisor;
    return { 
        remainder: rem, 
        quotient: (dividend - rem) / divisor
    };
}
bzim
quelle
0

Wenn Sie den Rest für sehr große Ganzzahlen berechnen müssen, die die JS-Laufzeit nicht als solche darstellen kann (jede Ganzzahl größer als 2 ^ 32 wird als Gleitkomma dargestellt und verliert daher an Genauigkeit), müssen Sie einen Trick ausführen.

Dies ist besonders wichtig, um viele Fälle von Scheckziffern zu überprüfen, die in vielen Fällen unseres täglichen Lebens vorhanden sind (Bankkontonummern, Kreditkarten, ...).

Zunächst benötigen Sie Ihre Nummer als Zeichenfolge (andernfalls haben Sie bereits an Genauigkeit verloren und der Rest macht keinen Sinn).

str = '123456789123456789123456789'

Sie müssen Ihre Zeichenfolge jetzt in kleinere Teile aufteilen, die klein genug sind, damit die Verkettung von Rest und Zeichenfolge in 9 Ziffern passt.

digits = 9 - String(divisor).length

Bereiten Sie einen regulären Ausdruck vor, um die Zeichenfolge zu teilen

splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g')

Wenn zum Beispiel digits7 ist, ist der reguläre Ausdruck

/.{1,7}(?=(.{7})+$)/g

Es entspricht einer nicht leeren Teilzeichenfolge mit der maximalen Länge 7, auf die (?=...)eine Anzahl von Zeichen folgt, die ein Vielfaches von 7 ist ( ein positiver Lookahead). Das 'g' soll den Ausdruck durch alle Zeichenfolgen laufen lassen und nicht bei der ersten Übereinstimmung anhalten.

Konvertieren Sie nun jeden Teil in eine Ganzzahl und berechnen Sie die verbleibenden Teile durch reduce(Addieren des vorherigen Rests - oder 0 - multipliziert mit der korrekten Potenz von 10):

reducer = (rem, piece) => (rem * Math.pow(10, digits) + piece) % divisor

Dies funktioniert aufgrund des Restalgorithmus "Subtraktion":

n mod d = (n - kd) mod d

Dies ermöglicht es, jeden 'Anfangsteil' der Dezimaldarstellung einer Zahl durch den Rest zu ersetzen, ohne den endgültigen Rest zu beeinflussen.

Der endgültige Code würde folgendermaßen aussehen:

function remainder(num, div) {
  const digits = 9 - String(div).length;
  const splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g');
  const mult = Math.pow(10, digits);
  const reducer = (rem, piece) => (rem * mult + piece) % div;

  return str.match(splitter).map(Number).reduce(reducer, 0);
}
umgeschrieben
quelle