¿División entera con resto en JavaScript?

Respuestas:

1240

Para un número yy un divisor, xcalcule el cociente ( quotient) y el resto ( remainder) como:

var quotient = Math.floor(y/x);
var remainder = y % x;
Mark Elliot
fuente
84
% funciona en flotantes en JavaScript (esto difiere de muchos otros lenguajes), lo que quizás no se desee: se 3.5 % 2evalúa a 1.5. Asegúrese de manejar (parseInt, floor, etc.) según sea necesario
16
La parte integral de -4.5 en matemáticas es -5, ya que -5 es el "número integral más alto posible que aún es inferior a -4.5".
Toughy
99
Sin embargo, sea lo que sea que decida hacer sobre los números negativos, debe ser coherente entre el cociente y el resto. Usar floory %juntos no es consistente de esa manera. Use en trunclugar de floor(lo que permite residuos negativos) o use la resta para obtener el resto ( rem = y - div * x).
Mark Reed
77
1. Si se va a calcular el resto remde todos modos, se puede obtener el cociente divmás rápido sin Solado: (y - rem) / x. 2. Por cierto, la operación de módulo según la definición recomendada por Donald Knuth (signo-coincidencias-divisor, no el resto, es decir, el módulo euclidiano, ni el signo-coincidencias-dividendo de JavaScript) es lo que podemos codificar en JavaScript function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }.
Aaron Mansheim
1
-9 / 2 = -4.5. Luego tomas el piso de -4.5, que es -5. Recuerde que -5 es menor que -4.5, y la operación de piso se define como el entero más grande menor que un valor dado.
Mark Reed
371

No soy un experto en operadores bit a bit, pero aquí hay otra forma de obtener el número entero:

var num = ~~(a / b);

Esto también funcionará correctamente para números negativos, mientras que Math.floor()se redondeará en la dirección incorrecta.

Esto parece correcto también:

var num = (a / b) >> 0;
usuario113716
fuente
84
Otro, cuyo propósito acabo de pasar los últimos 20 minutos tratando de resolver, aparentemente esa/b | 0
BlueRaja - Danny Pflughoeft
18
@ user113716 @BlueRaja Las operaciones bit a bit solo tienen sentido en tipos enteros y JS (por supuesto) lo sabe. ~~int, int | 0y int >> 0no modifica el argumento inicial, pero hace que el intérprete pase la parte integral al operador.
Aleksei Zabrodskii
15
floordifícilmente se redondea en la dirección incorrecta, dado su nombre, ¡aunque no es la dirección que la gente generalmente quiere!
Mark K Cowan
19
Eso es un buu buu. a = 12447132275286670000; b = 128 Math.floor(a/b)-> 97243220900677100y ~~(a/b)-> -1231452688.
Mirek Rusin
77
Ten cuidado con la precedencia. ~~(5/2) --> 2como lo hace (5/2)>>0 --> 2, pero ~~(5/2) + 1 --> 3, mientras ~~(5/2)>>0 + 1 --> 1. ~~Es una buena opción porque la precedencia es más apropiada.
timkay
218

Hice algunas pruebas de velocidad en 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

Lo anterior se basa en 10 millones de ensayos para cada uno.

Conclusión: El uso (a/b>>0)(o (~~(a/b))o (a/b|0)) para lograr el aumento de alrededor del 20% en la eficiencia. También tenga en cuenta que todos son inconsistentes con Math.floorcuándo a/b<0 && a%b!=0.

KalEl
fuente
54
Tenga en cuenta que optimizar la división de enteros para la velocidad tendría sentido solo si lo está haciendo mucho . En cualquier otro caso, recomendaría elegir el más simple (el que parezca más simple para usted y sus compañeros de trabajo).
mik01aj
77
@ m01 totalmente de acuerdo - hay demasiado énfasis en cosas como esta en línea
JonnyRaa
2
@ m01 Pero, ¿qué es más difícil: conocer Math.floory conocer las otras funciones API de quién sabe cuántos, o aprender sobre el ~operador (bit a bit) y cómo funcionan las operaciones a nivel de bit en JS y luego comprender el efecto de la doble tilde?
Stijn de Witt
11
Bueno, si sus compañeros de trabajo no están programando chips en ensamblador, probablemente lo entenderán Math.floormejor. E incluso si no, este es googleable.
mik01aj
2
@MarkGreen sí, pero solo querer hacer eso no significa que debas escribirlo de forma extraña solo porque resulta ser el más rápido, sin una buena razón: la claridad del código en general normalmente debería ser tu primera preocupación. Además, estas pruebas pueden ser completamente sin sentido después de los cambios de idioma y en diferentes navegadores, independientemente de lo que necesite, haga un perfil para descubrir qué es lento en su aplicación. ¡Es poco probable que sea su método de división de enteros a menos que ya haya optimizado alguna otra cosa!
JonnyRaa
150

ES6 presenta el nuevo Math.truncmétodo. Esto permite corregir la respuesta de @ MarkElliot para que funcione también para números negativos:

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

Tenga en cuenta que los Mathmétodos tienen la ventaja sobre los operadores bit a bit de que trabajan con números superiores a 2 31 .

Oriol
fuente
var y = 18014398509481984; var x = 5; div =? - insecto ?
4esn0k
44
@ 4esn0k No es un error. Su número tiene demasiados dígitos, no puede tener tanta precisión en un número IEEE 754 de formato binario de 64 bits. Por ejemplo, 18014398509481984 == 18014398509481985.
Oriol
18014398509481984 == 2 ** 54, y utilicé especialmente este número, porque está representado exactamente en formato binario64. y la respuesta también está representada exactamente
4esn0k
1
Creo que la elección es simple: ¿necesita soporte para números de hasta 32 bits con signo? Uso ~~(x/y). ¿Necesita admitir números mayores de hasta 54 bits con signo? Úselo Math.truncsi lo tiene, o de lo Math.floorcontrario (correcto para los números negativos). ¿Necesitas soportar números aún mayores? Use alguna biblioteca de números grandes.
Stijn de Witt
44
para los rubíes aquí de google en busca de divmod, puede implementarlo como tal: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
fuente
1
Lamentablemente, esta versión falla la prueba cuando x = -100 porque devuelve -34 en lugar de -33.
Samuel
1
¿qué pasa con "var x = 0.3; var y = 0.01;" ? (gracias a github.com/JuliaLang/julia/issues/4156#issuecomment-23324163 )
4esn0k
En realidad, @Samuel, con valores negativos, este método devuelve resultados correctos, o al menos devuelve los mismos valores que el método que usa Math.trunc:). Lo comprobé con 100,3; -100,3; 100, -3 y -100, -3. Por supuesto, ha pasado mucho tiempo desde su comentario y las cosas cambian.
Marjan Venema
19

Normalmente uso:

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

Probablemente no sea el más elegante, pero funciona.

Wolf Elkan
fuente
1
Buena solución porque evita la fealdad de analizar o truncar un flotador.
Dem Pilafian
66
si necesita cociente y resto, calcule primero el resto y luego reutilice ese valor en la expresión para el cociente, es decir, cociente = (a - resto) / b;
gb96
2
resto = a% b; cociente = (a - resto) / b;
Zv_oDD
16

Puede usar la función parseIntpara obtener un resultado truncado.

parseInt(a/b)

Para obtener un resto, use el operador mod:

a%b

parseInt tiene algunas dificultades con las cadenas, para evitar usar el parámetro radix con base 10

parseInt("09", 10)

En algunos casos, la representación de cadena del número puede ser una notación científica, en este caso, parseInt producirá un resultado incorrecto.

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

Esta llamada producirá 1 como resultado.

Édipo Costa Rebouças
fuente
77
parseIntdebe evitarse cuando sea posible. Aquí está la advertencia de Douglas Crockford: "Si el primer carácter de la cadena es 0, entonces la cadena se evalúa en la base 8 en lugar de la base 10. En la base 8, 8 y 9 no son dígitos, entonces parseInt (" 08 ") y parseInt ("09") produce 0 como resultado. Este error causa problemas en los programas que analizan fechas y horas. Afortunadamente, parseInt puede tomar un parámetro de raíz, por lo que parseInt ("08", 10) produce 8. Recomiendo que siempre proporcionar el parámetro radix ". archive.oreilly.com/pub/a/javascript/excerpts/…
Poderes
3
En una división, espero recibir un número, no una cadena, pero este es un buen punto.
Édipo Costa Rebouças
2
@Powers, así que agrega la raíz. Él no dice parseIntque se debe evitar; solo que hay algunas trampas a tener en cuenta. Debe ser consciente de estas cosas y estar preparado para hacer frente.
Ninguno
2
Nunca llame parseIntcon un argumento de número. parseIntse supone que analiza cadenas parcialmente numéricas, no trunca números.
Oriol
1
El hecho de que las cosas no estén destinadas originalmente para usarse de cierta manera, no significa que no debas hacerlo. Esta respuesta funciona.
fregante
6

JavaScript calcula correctamente el piso de números negativos y el resto de números no enteros, siguiendo las definiciones matemáticas para ellos.

FLOOR se define como "el número entero más grande más pequeño que el parámetro", por lo tanto:

  • números positivos: FLOOR (X) = parte entera de X;
  • números negativos: FLOOR (X) = parte entera de X menos 1 (porque debe ser MÁS PEQUEÑO que el parámetro, es decir, ¡más negativo!)

RESTANTE se define como el "sobrante" de una división (aritmética euclidiana). Cuando el dividendo no es un número entero, el cociente generalmente tampoco es un número entero, es decir, no hay resto, pero si el cociente se ve obligado a ser un número entero (y eso es lo que sucede cuando alguien intenta obtener el resto o el módulo de un número de punto flotante), obviamente habrá un "entero sobrante" no entero.

JavaScript calcula todo como se esperaba, por lo que el programador debe tener cuidado de hacer las preguntas adecuadas (¡y las personas deben tener cuidado de responder lo que se pregunta!) La primera pregunta de Yarin NO fue "¿cuál es la división entera de X por Y", pero, en su lugar, "TODO el número de veces que un entero dado entra en otro". Para números positivos, la respuesta es la misma para ambos, pero no para números negativos, porque la división entera (dividendo por divisor) será -1 menor que las veces que un número (divisor) "entra" en otro (dividendo). En otras palabras, FLOOR devolverá la respuesta correcta para una división entera de un número negativo, ¡pero Yarin no preguntó eso!

gammax respondió correctamente, ese código funciona según lo solicitado por Yarin. Por otro lado, Samuel está equivocado, no hizo los cálculos, supongo, o habría visto que funciona (también, no dijo cuál era el divisor de su ejemplo, pero espero que fuera 3):

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

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

Por cierto, probé el código en Firefox 27.0.1, funcionó como se esperaba, con números positivos y negativos y también con valores no enteros, tanto para dividendos como para divisores. Ejemplo:

-100.34 / 3.57: Entra = -28, Resto = -0.3800000000000079

Sí, me di cuenta, hay un problema de precisión allí, pero no tuve tiempo de verificarlo (no sé si es un problema con Firefox, Windows 7 o con la FPU de mi CPU). Sin embargo, para la pregunta de Yarin, que solo involucra números enteros, el código del gammax funciona perfectamente.

Cyberknight
fuente
5

Math.floor(operation) devuelve el valor redondeado hacia abajo de la operación.

Ejemplo de pregunta:

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

console.log(z);

Consola:

15

Ejemplo de segunda pregunta:

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

console.log(x);

Consola:

4 4

Aetricity
fuente
2

Se puede calcular el número de páginas en un solo paso: Math.ceil (x / y)

usuario1920925
fuente
No veo cómo esto proporciona el resto.
Paul Rooney
1

El comentario de Alex Moore-Niemi como respuesta:

Para Rubyists aquí de Google en busca de divmod, puede implementarlo como tal:

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

Resultado:

// [2, 33]
Alex
fuente
2
Por lo general, divmodutiliza la división en pisos ( Math.floor), que difiere de la división truncada ( Math.trunc) cuando están involucrados números negativos. Este es el caso del paquete NPMdivmod , Rubydivmod , SWI-Prologdivmod y probablemente también muchas otras implementaciones.
Palec
La división truncada ofrece resultados de aspecto más natural que la división en pisos, pero la compatibilidad supera a eso, en mi opinión. Tal vez, también hay razones matemáticas o de rendimiento para usar la división de pisos. Tenga en cuenta que, divmodpor lo general, existe porque se realiza dos veces más rápido que calcular las dos operaciones por separado. Proporcionar dicha función sin este beneficio de rendimiento puede ser confuso.
Palec
1

Si solo está dividiendo con potencias de dos, puede usar operadores 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];
}

(El primero es el cociente, el segundo el resto)

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

También puede usar ternary para decidir cómo manejar valores enteros positivos y negativos.

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

Si el número es positivo, todo está bien. Si el número es negativo, agregará 1 debido a cómo Math.floor maneja los negativos.

John Galt
fuente
0

Esto siempre se truncará hacia cero. No estoy seguro si es demasiado tarde, pero aquí 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
    };
}
bzim
fuente
0

Si necesita calcular el resto para enteros muy grandes, que el tiempo de ejecución JS no puede representar como tal (cualquier entero mayor que 2 ^ 32 se representa como flotante y por lo tanto pierde precisión), debe hacer algún truco.

Esto es especialmente importante para verificar muchos casos de dígitos de cheques que están presentes en muchos casos de nuestra vida diaria (números de cuenta bancaria, tarjetas de crédito, ...)

En primer lugar, necesita su número como una cadena (de lo contrario, ya ha perdido precisión y el resto no tiene sentido).

str = '123456789123456789123456789'

Ahora debe dividir su cadena en partes más pequeñas, lo suficientemente pequeñas como para que la concatenación de cualquier resto y un trozo de cuerda pueda caber en 9 dígitos.

digits = 9 - String(divisor).length

Prepare una expresión regular para dividir la cadena.

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

Por ejemplo, si digitses 7, la expresión regular es

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

Coincide con una subcadena no vacía de longitud máxima 7, que es seguida ( (?=...)es una anticipación positiva) por un número de caracteres que es múltiplo de 7. La 'g' es hacer que la expresión se ejecute a través de todas las cadenas, sin detenerse en la primera coincidencia.

Ahora convierta cada parte a entero, y calcule los restos por reduce(sumando el resto anterior - o 0 - multiplicado por la potencia correcta de 10):

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

Esto funcionará debido al algoritmo de resto de "resta":

n mod d = (n - kd) mod d

que permite reemplazar cualquier 'parte inicial' de la representación decimal de un número con su resto, sin afectar el resto final.

El código final se vería así:

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