Divisione intera con resto in JavaScript?


Risposte:


1240

Per alcuni numeri ye alcuni divisori, xcalcolare il quoziente ( quotient) e il resto ( remainder) come:

var quotient = Math.floor(y/x);
var remainder = y % x;

84
% funziona su float in JavaScript (questo differisce da molte altre lingue), il che forse non è desiderato: 3.5 % 2valuta 1.5. Assicurati di gestire (analisi, piano, ecc.) Come richiesto

16
La parte integrale di -4,5 in matematica è -5, poiché -5 è il "numero integrale più alto possibile che è ancora inferiore a -4,5".
Toughy,

9
Tuttavia, qualunque cosa tu decida di fare riguardo ai numeri negativi, dovrebbe essere coerente tra il quoziente e il resto. L'uso floore %insieme non sono coerenti in questo modo. Utilizzare truncinvece di floor(consentendo in tal modo i resti negativi) o utilizzare la sottrazione per ottenere il resto ( rem = y - div * x).
Mark Reed,

7
1. Se si sta andando a calcolare il resto remin ogni caso, è possibile ottenere il quoziente divpiù velocemente senza pavimentazione: (y - rem) / x. 2. A proposito dell'operazione modulo secondo la definizione raccomandata da Donald Knuth (segno-fiammifero-divisore, non il resto cioè modulo euclideo, né il segno-fiammifero-segno dividendo di JavaScript) è ciò che possiamo codificare in JavaScript come function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }.
Aaron Mansheim,

1
-9 / 2 = -4,5. Quindi prendi il piano di -4,5, che è -5. Ricordare che -5 è inferiore a -4,5 e che l'operazione del pavimento è definita come il numero intero più grande inferiore a un determinato valore.
Mark Reed,

371

Non sono un esperto di operatori bit a bit, ma ecco un altro modo per ottenere l'intero numero:

var num = ~~(a / b);

Questo funzionerà correttamente anche per i numeri negativi, mentre Math.floor()arrotonderà nella direzione sbagliata.

Anche questo sembra corretto:

var num = (a / b) >> 0;

84
Un altro, il cui scopo ho appena trascorso gli ultimi 20 minuti cercando di capire, è apparentementea/b | 0
BlueRaja - Danny Pflughoeft

18
@ user113716 @BlueRaja Le operazioni bit a bit hanno senso solo su tipi interi e JS (ovviamente) lo sa. ~~int, int | 0E int >> 0non modifica argomento iniziale, ma far passare interprete parte integrante all'operatore.
Aleksei Zabrodskii,

15
floordifficilmente gira nella direzione sbagliata, dato il suo nome, ma non la direzione che la gente generalmente vuole però!
Mark K Cowan,

19
Questo è un buu buu. a = 12447132275286670000; b = 128 Math.floor(a/b)-> 97243220900677100e ~~(a/b)-> -1231452688.
Mirek Rusin,

7
Stai attento con la precedenza. ~~(5/2) --> 2come fa (5/2)>>0 --> 2, ma ~~(5/2) + 1 --> 3, mentre ~~(5/2)>>0 + 1 --> 1. ~~è una buona scelta perché la precedenza è più appropriata.
timkay,

217

Ho fatto alcuni test di velocità su Firefox.

-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

Quanto sopra si basa su 10 milioni di prove per ciascuno.

Conclusione: utilizzare (a/b>>0)(o (~~(a/b))o (a/b|0)) per ottenere circa il 20% di guadagno in termini di efficienza. Inoltre, tieni presente che sono tutti incompatibili con Math.floor, quando a/b<0 && a%b!=0.


54
Nota che l'ottimizzazione della divisione di interi per la velocità avrebbe senso solo se lo stai facendo molto . In ogni altro caso, consiglierei di scegliere quello più semplice (quello che sembra più semplice per te e i tuoi colleghi).
mik01aj,

7
@ m01 sono totalmente d'accordo - c'è troppa attenzione a cose come questa online
JonnyRaa,

2
@ m01 Ma ciò che è più difficile: conoscere Math.floore chissà quante altre funzioni API, o conoscere l' ~operatore (bit a bit) e come funzionano le operazioni bit a bit in JS e quindi comprendere l'effetto della doppia tilde?
Stijn de Witt,

11
Bene, se i tuoi colleghi non stanno programmando i chip nell'assemblatore, probabilmente capiranno Math.floormeglio. E anche se no, questo è googleable.
mik01aj,

2
@MarkGreen sì, ma volere solo questo non significa che dovresti scriverlo in una forma strana solo perché sembra essere più veloce, senza una buona ragione - la chiarezza del codice in generale dovrebbe essere la tua prima preoccupazione. Inoltre, questi test potrebbero essere del tutto privi di significato dopo il cambio di lingua e su diversi browser: dovresti creare un profilo per scoprire cosa è lento nella tua applicazione, indipendentemente. È improbabile che sia il tuo metodo di divisione intera a meno che tu non abbia già ottimizzato ogni altra cosa!
JonnyRaa,

150

ES6 introduce il nuovo Math.truncmetodo. Ciò consente di correggere la risposta di @ MarkElliot per farlo funzionare anche con numeri negativi:

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

Si noti che i Mathmetodi hanno il vantaggio rispetto agli operatori bit a bit di lavorare con numeri oltre 2 31 .


var y = 18014398509481984; var x = 5; div =? - bug?
4

4
@ 4esn0k Non è un bug. Il tuo numero ha troppe cifre, non puoi avere tanta precisione in un numero IEEE 754 in formato binario a 64 bit. Ad esempio 18014398509481984 == 18014398509481985,.
Oriol,

18014398509481984 == 2 ** 54 e ho utilizzato questo numero appositamente perché è rappresentato esattamente in formato binario64. e anche la risposta è rappresentata esattamente
4esn0k

1
Penso che la scelta sia semplice: hai bisogno del supporto per numeri fino a 32 bit firmati? Usa ~~(x/y). Hai bisogno di supportare numeri più grandi fino a 54 bit firmati? Usa Math.truncse ce l'hai, o Math.flooraltrimenti (corretto per i numeri negativi). Hai bisogno di supportare numeri ancora più grandi? Usa una libreria di grandi numeri.
Stijn de Witt,

4
per i rubyists qui da google alla ricerca di divmod, puoi implementarlo come tale: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;

1
Questa versione sfortunatamente fallisce il test quando x = -100 perché restituisce -34 invece di -33.
Samuel,

1
che dire di "var x = 0.3; var y = 0.01;" ? (grazie a github.com/JuliaLang/julia/issues/4156#issuecomment-23324163 )
4esn0k

In realtà, @Samuel, con valori negativi, questo metodo restituisce risultati corretti, o almeno restituisce gli stessi valori del metodo usando Math.trunc:). Ho controllato con 100,3; -100,3; 100, -3 e -100, -3. Certo, è passato molto tempo dal tuo commento e le cose cambiano.
Marjan Venema,

19

Di solito uso:

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

Probabilmente non è il più elegante, ma funziona.


1
Bella soluzione perché evita la bruttezza di analizzare o troncare un galleggiante.
Dem Pilafian,

6
se sono necessari sia il quoziente sia il resto, calcolare prima il resto e quindi riutilizzare quel valore nell'espressione per il quoziente, ovvero quoziente = (a - resto) / b;
gb96

2
resto = a% b; quoziente = (a - resto) / b;
Zv_oDD,

16

È possibile utilizzare la funzione parseIntper ottenere un risultato troncato.

parseInt(a/b)

Per ottenere un resto, usa l'operatore mod:

a%b

parseInt ha alcune insidie ​​con stringhe, per evitare l'uso del parametro radix con base 10

parseInt("09", 10)

In alcuni casi la rappresentazione in forma di stringa del numero può essere una notazione scientifica, in questo caso parseInt produrrà un risultato errato.

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

Questa chiamata produrrà 1 come risultato.


7
parseIntdovrebbe essere evitato quando possibile. Ecco l'avvertimento di Douglas Crockford: "Se il primo carattere della stringa è 0, la stringa viene valutata nella base 8 anziché nella base 10. Nella base 8, 8 e 9 non sono cifre, quindi parseInt (" 08 ") e parseInt ("09") produce 0 come risultato. Questo errore causa problemi nei programmi che analizzano date e ore. Fortunatamente, parseInt può prendere un parametro radix, in modo che parseInt ("08", 10) produca 8. Ti consiglio sempre fornire il parametro radix. " archive.oreilly.com/pub/a/javascript/excerpts/…
Poteri

3
In una divisione, mi aspetto di ricevere un numero, non una stringa, ma questo è un buon punto.
Édipo Costa Rebouças,

2
@ Poteri quindi aggiungi il radix. Non dice che parseIntdovrebbe essere evitato; solo che ci sono alcuni aspetti da tenere presente. Devi essere consapevole di queste cose ed essere pronto a far fronte.
Nessuno il

2
Non chiamare mai parseIntcon un argomento numerico. parseIntdovrebbe analizzare stringhe parzialmente numeriche, non troncare i numeri.
Oriol,

1
Solo perché originariamente le cose non sono pensate per essere usate in un certo modo, non significa che non dovresti. Questa risposta funziona.
fregante

6

JavaScript calcola il pavimento dei numeri negativi e il resto dei numeri non interi, seguendo le definizioni matematiche per essi.

FLOOR è definito come "il numero intero più grande più piccolo del parametro", quindi:

  • numeri positivi: PAVIMENTO (X) = parte intera di X;
  • numeri negativi: FLOOR (X) = parte intera di X meno 1 (perché deve essere PIÙ PICCOLO del parametro, cioè più negativo!)

REMAINDER è definito come il "residuo" di una divisione (aritmetica euclidea). Quando il dividendo non è un numero intero, di solito anche il quoziente non è un numero intero, ovvero non vi è alcun resto, ma se il quoziente è costretto a essere un numero intero (ed è quello che succede quando qualcuno cerca di ottenere il resto o il modulo di un numero in virgola mobile), ci sarà un "numero intero" non intero, ovviamente.

JavaScript calcola tutto come previsto, quindi il programmatore deve fare attenzione a porre le domande giuste (e le persone dovrebbero fare attenzione a rispondere a ciò che viene chiesto!) La prima domanda di Yarin NON era "qual è la divisione intera di X per Y", ma, invece "TUTTO il numero di volte in cui un dato intero VA IN UN altro". Per i numeri positivi, la risposta è la stessa per entrambi, ma non per i numeri negativi, perché la divisione intera (dividendo per divisore) sarà -1 più piccola delle volte in cui un numero (divisore) "entra" in un altro (dividendo). In altre parole, FLOOR restituirà la risposta corretta per una divisione intera di un numero negativo, ma Yarin non l'ha chiesto!

gammax ha risposto correttamente, quel codice funziona come richiesto da Yarin. D'altra parte, Samuel ha torto, non ha fatto la matematica, immagino, o avrebbe visto che funziona (inoltre, non ha detto quale fosse il divisore del suo esempio, ma spero che lo fosse 3):

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

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

A proposito, ho testato il codice su Firefox 27.0.1, ha funzionato come previsto, con numeri positivi e negativi e anche con valori non interi, sia per i dividendi che per i divisori. Esempio:

-100.34 / 3.57: va in = -28, resto = -0.3800000000000079

Sì, ho notato, c'è un problema di precisione lì, ma non ho avuto il tempo di controllarlo (non so se si tratta di un problema con Firefox, Windows 7 o con la FPU della mia CPU). Per la domanda di Yarin, tuttavia, che coinvolge solo numeri interi, il codice gammax funziona perfettamente.


5

Math.floor(operation) restituisce il valore arrotondato per difetto dell'operazione.

Esempio di 1 ° domanda:

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

console.log(z);

Console:

15

Esempio di seconda domanda:

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

console.log(x);

Console:

4


2

Il calcolo del numero di pagine può essere eseguito in un solo passaggio: Math.ceil (x / y)


Non vedo come questo fornisce il resto.
Paul Rooney,

1

Il commento di Alex Moore-Niemi come risposta:

Per i rubyisti qui da Google in cerca di divmod, puoi implementarlo come tale:

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

Risultato:

// [2, 33]

2
Di solito, divmodutilizza la divisione floored ( Math.floor), che differisce dalla divisione troncata ( Math.trunc) quando sono coinvolti numeri negativi. Questo è il caso del pacchetto NPMdivmod , Rubydivmod , SWI-Prologdivmod e probabilmente anche molte altre implementazioni.
Palec,

La divisione troncata offre risultati più naturali rispetto alla divisione pavimentata, ma la compatibilità supera quella dell'IMO. Forse, ci sono anche ragioni matematiche o prestazionali per l'uso della divisione floored. Si noti che di solito divmodesiste perché è due volte più veloce rispetto al calcolo separato delle due operazioni. Fornire una tale funzione senza questo vantaggio prestazionale potrebbe essere fonte di confusione.
Palec,

1

Se stai solo dividendo con potenze di due, puoi usare operatori bit a bit:

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

(Il primo è il quoziente, il secondo il resto)


In generale, function divideByPowerOf2(num, exponent) { return [num >> exponent, num & ((1 << exponent) - 1)]; }.
Palec,

0

Puoi usare ternary per decidere come gestire anche i valori interi positivi e negativi.

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

Se il numero è positivo, va tutto bene. Se il numero è negativo, verrà aggiunto 1 a causa di come Math.floor gestisce i negativi.


0

Questo troncerà sempre verso zero. Non sono sicuro che sia troppo tardi, ma qui va:

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

0

Se devi calcolare il resto per numeri interi molto grandi, che il runtime JS non può rappresentare come tale (qualsiasi numero intero maggiore di 2 ^ 32 è rappresentato come un float e quindi perde precisione), devi fare qualche trucco.

Ciò è particolarmente importante per controllare molti casi di cifre di controllo che sono presenti in molti casi della nostra vita quotidiana (numeri di conto bancario, carte di credito, ...)

Prima di tutto hai bisogno del tuo numero come stringa (altrimenti hai già perso la precisione e il resto non ha senso).

str = '123456789123456789123456789'

Ora è necessario dividere la stringa in parti più piccole, abbastanza piccole da consentire la concatenazione di qualsiasi resto e un pezzo di stringa in 9 cifre.

digits = 9 - String(divisor).length

Prepara un'espressione regolare per dividere la stringa

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

Ad esempio, se digitsè 7, regexp è

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

Corrisponde a una sottostringa non vuota di lunghezza massima 7, che è seguita ( (?=...)è un punto di vista positivo) da un numero di caratteri che è multiplo di 7. La 'g' è di far scorrere l'espressione attraverso tutta la stringa, senza fermarsi alla prima corrispondenza.

Ora converti ogni parte in numero intero e calcola i resti per reduce(aggiungendo nuovamente il resto precedente - o 0 - moltiplicato per la potenza corretta di 10):

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

Questo funzionerà a causa dell'algoritmo di resto "sottrazione":

n mod d = (n - kd) mod d

che consente di sostituire qualsiasi "parte iniziale" della rappresentazione decimale di un numero con il suo resto, senza influire sul resto finale.

Il codice finale sarebbe simile a:

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.