Division entière avec reste en JavaScript?

Réponses:

1240

Pour un certain nombre yet un certain diviseur, xcalculez le quotient ( quotient) et le reste ( remainder) comme:

var quotient = Math.floor(y/x);
var remainder = y % x;
Mark Elliot
la source
84
% fonctionne sur des flotteurs en JavaScript (ce qui diffère de nombreux autres langages), ce qui n'est peut-être pas souhaité: 3.5 % 2évalue à 1,5. Assurez-vous de gérer (parseInt, sol, etc.) comme requis
16
La partie intégrante de -4,5 en mathématiques est -5, car -5 est le "nombre entier le plus élevé possible qui est toujours inférieur à -4,5".
Toughy
9
Cependant, quoi que vous décidiez de faire à propos des nombres négatifs, cela devrait être cohérent entre le quotient et le reste. Utiliser flooret %ensemble n'est pas cohérent de cette façon. Soit utiliser à la truncplace de floor(permettant ainsi des restes négatifs) ou utiliser la soustraction pour obtenir le reste ( rem = y - div * x).
Mark Reed
7
1. Si vous allez calculer le reste de remtoute façon, vous pouvez obtenir le quotient divplus rapidement , sans revêtement de sol: (y - rem) / x. 2. Au fait, l'opération modulo par la définition recommandée de Donald Knuth (signe-correspond-diviseur, pas le reste c'est-à-dire module euclidien, ni le signe-correspond-dividende JavaScript) est ce que nous pouvons coder en JavaScript comme function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }.
Aaron Mansheim
1
-9 / 2 = -4,5. Vous prenez alors le plancher de -4,5, soit -5. N'oubliez pas que -5 est inférieur à -4,5 et que l'opération de plancher est définie comme le plus grand entier inférieur à une valeur donnée.
Mark Reed
371

Je ne suis pas un expert des opérateurs au niveau du bit, mais voici une autre façon d'obtenir le nombre entier:

var num = ~~(a / b);

Cela fonctionnera également correctement pour les nombres négatifs, tandis que Math.floor() en arrondissant dans la mauvaise direction.

Cela semble également correct:

var num = (a / b) >> 0;
user113716
la source
84
Un autre, dont je viens de passer le but des 20 dernières minutes à essayer de comprendre, est apparemmenta/b | 0
BlueRaja - Danny Pflughoeft
18
@ user113716 @BlueRaja Les opérations au niveau du bit n'ont de sens que sur les types entiers et JS (bien sûr) le sait. ~~int, int | 0et int >> 0ne modifie pas l'argument initial, mais oblige l'interpréteur à passer la partie intégrante à l'opérateur.
Aleksei Zabrodskii
15
floorà peine dans la mauvaise direction, étant donné son nom - mais pas dans la direction que les gens veulent généralement!
Mark K Cowan
19
C'est un buu buu. a = 12447132275286670000; b = 128 Math.floor(a/b)-> 97243220900677100et ~~(a/b)-> -1231452688.
Mirek Rusin
7
Soyez prudent avec la priorité. ~~(5/2) --> 2comme le fait (5/2)>>0 --> 2, mais ~~(5/2) + 1 --> 3, tout ~~(5/2)>>0 + 1 --> 1. ~~est un bon choix car la priorité est plus appropriée.
timkay
218

J'ai fait des tests de vitesse sur 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

Ce qui précède est basé sur 10 millions d'essais pour chacun.

Conclusion: utilisez (a/b>>0)(ou (~~(a/b))ou (a/b|0)) pour obtenir un gain d'efficacité d'environ 20%. Gardez également à l'esprit qu'ils sont tous incompatibles avec Math.floor, quand a/b<0 && a%b!=0.

KalEl
la source
54
Notez que l'optimisation de la division entière pour la vitesse n'aurait de sens que si vous le faites beaucoup . Dans tous les autres cas, je recommanderais de choisir le plus simple (celui qui vous semble le plus simple et celui de vos collègues).
mik01aj
7
@ m01 tout à fait d'accord - il y a beaucoup trop d'attention sur des trucs comme ça en ligne
JonnyRaa
2
@ m01 Mais ce qui est plus difficile: en savoir plus sur Math.floorles autres fonctions de l'API et qui sait qui, ou en savoir plus sur l' ~opérateur (bit à bit) et sur le fonctionnement des opérations bit à bit dans JS, puis comprendre l'effet du double tilde?
Stijn de Witt
11
Eh bien, si vos collègues ne programment pas de puces dans l'assembleur, ils comprendront probablement Math.floormieux. Et même si ce n'est pas le cas, celui-ci est googleable.
mik01aj
2
@MarkGreen oui mais juste vouloir le faire ne signifie pas que vous devriez l'écrire sous une forme étrange juste parce que c'est le plus rapide, sans raison valable - la clarté du code en général devrait normalement être votre première préoccupation. De plus, ces tests peuvent être complètement dénués de sens après des changements de langue et sur différents navigateurs - vous auriez besoin de profiler pour savoir ce qui est lent dans votre application. Il est peu probable que ce soit votre méthode de division entière, sauf si vous avez déjà optimisé une autre chose!
JonnyRaa
150

ES6 introduit la nouvelle Math.truncméthode. Cela permet de corriger la réponse de @ MarkElliot pour qu'elle fonctionne également pour les nombres négatifs:

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

Notez que les Mathméthodes ont l'avantage sur les opérateurs au niveau du bit qu'elles fonctionnent avec des nombres supérieurs à 2 31 .

Oriol
la source
var y = 18014398509481984; var x = 5; div =? - punaise ?
4esn0k
4
@ 4esn0k Ce n'est pas un bug. Votre numéro comporte trop de chiffres, vous ne pouvez pas avoir autant de précision dans un numéro IEEE 754 au format binaire 64 bits. Par exemple 18014398509481984 == 18014398509481985,.
Oriol
18014398509481984 == 2 ** 54, et j'ai spécialement utilisé ce nombre, car il est représenté exactement au format binaire64. et la réponse est représentée exactement aussi
4esn0k
1
Je pense que le choix est simple: vous avez besoin d'un support pour les numéros jusqu'à 32 bits signés? Utilisez ~~(x/y). Besoin de prendre en charge de plus grands nombres jusqu'à 54 bits signés? Utilisez-le Math.truncsi vous l'avez, ou Math.floorautrement (corrigez les nombres négatifs). Besoin de prendre en charge des nombres encore plus importants? Utilisez une grande bibliothèque de nombres.
Stijn de Witt
4
pour les rubyistes ici de Google à la recherche de divmod, vous pouvez l'implémenter comme tel: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
la source
1
Cette version échoue malheureusement au test lorsque x = -100 car elle renvoie -34 au lieu de -33.
Samuel
1
qu'en est-il de "var x = 0,3; var y = 0,01;" ? (merci à github.com/JuliaLang/julia/issues/4156#issuecomment-23324163 )
4esn0k
En fait, @Samuel, avec des valeurs négatives, cette méthode renvoie des résultats corrects, ou du moins elle renvoie les mêmes valeurs que la méthode utilisant Math.trunc:). J'ai vérifié avec 100,3; -100,3; 100, -3 et -100, -3. Bien sûr, beaucoup de temps s'est écoulé depuis votre commentaire et les choses changent.
Marjan Venema
19

J'utilise normalement:

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

Ce n'est probablement pas le plus élégant, mais ça marche.

Wolf Elkan
la source
1
Belle solution car elle évite la laideur d'analyser ou de tronquer un flotteur.
Dem Pilafian
6
si vous avez besoin à la fois de quotient et de reste, calculez d'abord le reste, puis réutilisez cette valeur dans l'expression pour le quotient, c'est-à-dire quotient = (a - reste) / b;
gb96
2
reste = a% b; quotient = (a - reste) / b;
Zv_oDD
16

Vous pouvez utiliser la fonction parseIntpour obtenir un résultat tronqué.

parseInt(a/b)

Pour obtenir un reste, utilisez l'opérateur mod:

a%b

parseInt a quelques pièges avec les chaînes, pour éviter d'utiliser le paramètre radix avec la base 10

parseInt("09", 10)

Dans certains cas, la représentation sous forme de chaîne du nombre peut être une notation scientifique, dans ce cas, parseInt produira un résultat incorrect.

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

Cet appel produira 1 comme résultat.

Édipo Costa Rebouças
la source
7
parseIntdoit être évité autant que possible. Voici l'avertissement de Douglas Crockford: "Si le premier caractère de la chaîne est 0, alors la chaîne est évaluée en base 8 au lieu de base 10. En base 8, 8 et 9 ne sont pas des chiffres, donc parseInt (" 08 ") et parseInt ("09") produit 0 comme résultat. Cette erreur provoque des problèmes dans les programmes qui analysent les dates et les heures. Heureusement, parseInt peut prendre un paramètre radix, de sorte que parseInt ("08", 10) produit 8. Je vous recommande de toujours fournir le paramètre radix. " archive.oreilly.com/pub/a/javascript/excerpts/…
Pouvoirs
3
Dans une division, je m'attends à recevoir un nombre, pas une chaîne, mais c'est un bon point.
Édipo Costa Rebouças
2
@Powers alors ajoutez le radix. Il ne dit pas qu'il parseIntfaut éviter; juste qu'il y a quelques pièges à connaître. Vous devez être conscient de ces choses et être prêt à y faire face.
Aucun
2
N'appelez jamais parseIntavec un argument numérique. parseIntest censé analyser des chaînes partiellement numériques, pas tronquer des nombres.
Oriol
1
Tout simplement parce que les choses ne sont pas initialement destinées à être utilisées d'une certaine manière, cela ne signifie pas que vous ne devriez pas. Cette réponse fonctionne.
fregante
6

JavaScript calcule à droite le plancher des nombres négatifs et le reste des nombres non entiers, en suivant les définitions mathématiques pour eux.

FLOOR est défini comme "le plus grand nombre entier inférieur au paramètre", ainsi:

  • nombres positifs: FLOOR (X) = partie entière de X;
  • nombres négatifs: FLOOR (X) = partie entière de X moins 1 (car il doit être PLUS PETIT que le paramètre, c'est-à-dire plus négatif!)

REMAINDER est défini comme le «reste» d'une division (arithmétique euclidienne). Lorsque le dividende n'est pas un entier, le quotient n'est généralement pas non plus un entier, c'est-à-dire qu'il n'y a pas de reste, mais si le quotient est forcé d'être un entier (et c'est ce qui se passe lorsque quelqu'un essaie d'obtenir le reste ou le module d'un nombre à virgule flottante), il restera évidemment un "non entier".

JavaScript calcule tout comme prévu, donc le programmeur doit faire attention à poser les bonnes questions (et les gens devraient faire attention à répondre à ce qui est demandé!) La première question de Yarin n'était PAS "quelle est la division entière de X par Y", mais, au lieu de cela, "le nombre entier de fois qu'un entier donné va dans un autre". Pour les nombres positifs, la réponse est la même pour les deux, mais pas pour les nombres négatifs, car la division entière (dividende par diviseur) sera -1 inférieure à la fois où un nombre (diviseur) "entre" dans un autre (dividende). En d'autres termes, FLOOR retournera la bonne réponse pour une division entière d'un nombre négatif, mais Yarin ne l'a pas demandé!

gammax a répondu correctement, ce code fonctionne comme demandé par Yarin. D'un autre côté, Samuel a tort, il n'a pas fait le calcul, je suppose, ou il aurait vu que ça marche (aussi, il n'a pas dit quel était le diviseur de son exemple, mais j'espère que c'était le cas) 3):

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

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

Soit dit en passant, j'ai testé le code sur Firefox 27.0.1, il a fonctionné comme prévu, avec des nombres positifs et négatifs et également avec des valeurs non entières, à la fois pour le dividende et le diviseur. Exemple:

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

Oui, j'ai remarqué qu'il y a un problème de précision, mais je n'ai pas eu le temps de le vérifier (je ne sais pas si c'est un problème avec Firefox, Windows 7 ou le FPU de mon CPU). Pour la question de Yarin, qui n'implique que des entiers, le code du gammax fonctionne parfaitement.

Cyberknight
la source
5

Math.floor(operation) renvoie la valeur arrondie de l'opération.

Exemple de 1 ère question:

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

console.log(z);

Console:

15

Exemple de 2 ème question:

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

console.log(x);

Console:

4

Aétricité
la source
2

Le calcul du nombre de pages peut être effectué en une seule étape: Math.ceil (x / y)

user1920925
la source
Je ne vois pas comment cela fournit le reste.
Paul Rooney
1

Alex Moore-NiemiCommentaire d' en réponse:

Pour les Rubyists ici de Google à la recherche de divmod, vous pouvez l'implémenter comme tel:

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

Résultat:

// [2, 33]
Alex
la source
2
Habituellement, les divmodutilisations division parqueté ( Math.floor), qui diffère de la division tronquée ( Math.trunc) lorsque sont impliqués des nombres négatifs. C'est le cas du package NPMdivmod , Rubydivmod , SWI-Prologdivmod et probablement de nombreuses autres implémentations.
Palec
La division tronquée donne des résultats plus naturels que la division au sol, mais la compatibilité l'emporte sur celle de l'OMI. Peut-être existe-t-il également des raisons mathématiques ou de performance pour utiliser la division par le sol. Notez qu'en général, divmodexiste car il est exécuté deux fois plus vite que le calcul séparé des deux opérations. Fournir une telle fonction sans cet avantage de performance peut être déroutant.
Palec
1

Si vous divisez simplement avec des puissances de deux, vous pouvez utiliser des opérateurs au niveau du 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];
}

(Le premier est le quotient, le second le reste)

Konstantin Möllers
la source
En général, function divideByPowerOf2(num, exponent) { return [num >> exponent, num & ((1 << exponent) - 1)]; }.
Palec
0

Vous pouvez également utiliser ternaire pour décider comment gérer les valeurs entières positives et négatives.

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

Si le nombre est positif, tout va bien. Si le nombre est négatif, il ajoutera 1 en raison de la façon dont Math.floor gère les négatifs.

John Galt
la source
0

Cela sera toujours tronqué vers zéro. Je ne sais pas s'il est trop tard, mais voilà:

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
la source
0

Si vous devez calculer le reste pour les très grands entiers, que le runtime JS ne peut pas représenter en tant que tels (tout entier supérieur à 2 ^ 32 est représenté comme un flottant et perd ainsi sa précision), vous devez faire un tour.

Ceci est particulièrement important pour vérifier de nombreux cas de chiffres de contrôle qui sont présents dans de nombreux cas de notre vie quotidienne (numéros de compte bancaire, cartes de crédit, ...)

Tout d'abord, vous avez besoin de votre numéro sous forme de chaîne (sinon vous avez déjà perdu la précision et le reste n'a pas de sens).

str = '123456789123456789123456789'

Vous devez maintenant diviser votre chaîne en parties plus petites, suffisamment petites pour que la concaténation de tout reste et un morceau de chaîne puisse tenir en 9 chiffres.

digits = 9 - String(divisor).length

Préparez une expression régulière pour diviser la chaîne

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

Par exemple, si digitsest 7, l'expression rationnelle est

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

Il correspond à une sous-chaîne non vide de longueur maximale 7, qui est suivie ( (?=...)est une anticipation positive) par un nombre de caractères qui est multiple de 7. Le `` g '' consiste à faire en sorte que l'expression passe à travers toute la chaîne, sans s'arrêter à la première correspondance.

Maintenant, convertissez chaque partie en entier et calculez les restes par reduce(en ajoutant le reste précédent - ou 0 - multiplié par la puissance correcte de 10):

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

Cela fonctionnera en raison de l'algorithme de reste "soustraction":

n mod d = (n - kd) mod d

ce qui permet de remplacer toute «partie initiale» de la représentation décimale d'un nombre par son reste, sans affecter le reste final.

Le code final ressemblerait à:

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);
}
réécrit
la source