Divisão inteira com restante em JavaScript?

929

Em JavaScript , como obtenho:

  1. O número inteiro de vezes que um número inteiro entra em outro?
  2. O restante?
Yarin
fonte

Respostas:

1239

Para alguns números ye divisores, xcalcule o quociente ( quotient) e o restante ( remainder) como:

var quotient = Math.floor(y/x);
var remainder = y % x;
Mark Elliot
fonte
84
% funciona em flutuadores em JavaScript (isso difere de muitos outros idiomas), o que talvez não seja desejado: é 3.5 % 2avaliado em 1,5. Certifique-se de manipular (análise, piso, etc.) conforme necessário #
16
A parte integrante de -4,5 em matemática é -5, como -5 é o "maior número integral possível ainda inferior a -4,5".
Toughy
9
No entanto, o que você decidir fazer sobre números negativos, deve ser consistente entre o quociente e o restante. O uso floore o %conjunto não são consistentes dessa maneira. Use em truncvez de floor(permitindo assim restos negativos) ou subtração para obter o restante ( rem = y - div * x).
Mark Reed
7
1. Se você estiver indo para calcular o restante remde qualquer maneira, você pode obter o quociente divmais rápido sem pavimentação: (y - rem) / x. 2. A propósito, a operação do módulo pela definição recomendada por Donald Knuth (sign-match-divisor, não o restante, isto é, módulo euclidiano, nem o JavaScript sign-match-dividend) é o que podemos codificar em JavaScript como function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }.
Aaron Mansheim
1
-9 / 2 = -4,5. Você então usa o piso de -4,5, que é -5. Lembre-se de que -5 é menor que -4,5 e a operação de piso é definida como o maior número inteiro menor que um determinado valor.
Mark Reed
371

Não sou especialista em operadores bit a bit, mas aqui está outra maneira de obter o número inteiro:

var num = ~~(a / b);

Isso funcionará corretamente também para números negativos, enquanto Math.floor()arredondará na direção errada.

Isso parece correto também:

var num = (a / b) >> 0;
user113716
fonte
84
Outro, cujo objetivo eu apenas passei os últimos 20 minutos tentando descobrir, é aparentementea/b | 0
BlueRaja - Danny Pflughoeft
18
@ user113716 @BlueRaja As operações bit a bit fazem sentido apenas em tipos inteiros e JS (é claro) sabe disso. ~~int, int | 0e int >> 0não modifica o argumento inicial, mas faz com que o intérprete passe parte integrante para o operador.
Aleksei Zabrodskii
15
floordificilmente gira na direção errada, dado o nome - mas não a direção que as pessoas geralmente querem!
Mark K Cowan
19
Isso é um buu buu. a = 12447132275286670000; b = 128 Math.floor(a/b)-> 97243220900677100e ~~(a/b)-> -1231452688.
Mirek Rusin
7
Tenha cuidado com precedência. ~~(5/2) --> 2como faz (5/2)>>0 --> 2, mas ~~(5/2) + 1 --> 3enquanto ~~(5/2)>>0 + 1 --> 1. ~~é uma boa escolha porque a precedência é mais apropriada.
timkay
217

Eu fiz alguns testes de velocidade no 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

O exposto acima é baseado em 10 milhões de tentativas para cada um.

Conclusão: Use (a/b>>0)(ou (~~(a/b))ou (a/b|0)) para obter um ganho de aproximadamente 20% em eficiência. Lembre-se também de que todos são inconsistentes com Math.floorquando a/b<0 && a%b!=0.

KalEl
fonte
54
Observe que otimizar a divisão inteira para velocidade só faria sentido se você estiver fazendo isso muito . Em qualquer outro caso, eu recomendaria escolher o mais simples (o que parecer mais simples para você e seus colegas de trabalho).
Mk01aj
7
@ m01 concordo totalmente - há muito foco em coisas como esta on
JonnyRaa
2
@ m01 Mas o que é mais difícil: aprender Math.floore quem sabe quantas outras funções da API, ou aprender sobre o ~operador (bit a bit) e como as operações bit a bit funcionam em JS e depois entender o efeito do double til?
Stijn de Witt
11
Bem, se seus colegas de trabalho não estiverem programando chips no assembler, eles provavelmente entenderão Math.floormelhor. E mesmo se não, este é googleable.
mik01aj
2
@ MarkGreen sim, mas apenas querer fazer isso não significa que você deve escrevê-lo de uma forma estranha, apenas porque acontece que é o mais rápido, sem uma boa razão - a clareza do código em geral deve normalmente ser sua primeira preocupação. Além disso, esses testes podem ser completamente sem sentido após alterações de idioma e em diferentes navegadores - você precisaria criar um perfil para descobrir o que é lento no seu aplicativo, independentemente. É improvável que seja o seu método de divisão inteira, a menos que você já tenha otimizado alguma outra coisa!
precisa saber é o seguinte
150

ES6 apresenta o novo Math.truncmétodo. Isso permite corrigir a resposta do @ MarkElliot para que funcione também com números negativos:

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

Observe que os Mathmétodos têm a vantagem sobre os operadores bit a bit de trabalharem com números acima de 2 31 .

Oriol
fonte
var y = 18014398509481984; var x = 5; div =? - erro ?
precisa saber é o seguinte
4
@ 4esn0k Não é um bug. Seu número tem muitos dígitos, você não pode ter tanta precisão em um número IEEE 754 de formato binário de 64 bits. Por exemplo 18014398509481984 == 18014398509481985,.
Oriol
18014398509481984 == 2 ** 54, e eu usei esse número especialmente porque é representado exatamente no formato binário64. ea resposta é representado exatamente demasiado
4esn0k
1
Eu acho que a escolha é simples: você precisa de suporte para números assinados até 32 bits? Use ~~(x/y). Precisa suportar números maiores de até 54 bits assinados? Use Math.truncse tiver, ou Math.floornão (corrija os números negativos). Precisa suportar números ainda maiores? Use uma grande biblioteca de números.
Stijn de Witt
4
para Rubistas aqui do Google em busca de divmod, você pode implementá-lo 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
fonte
1
Infelizmente, esta versão falha no teste quando x = -100 porque retorna -34 em vez de -33.
Samuel
1
que tal "var x = 0,3; var y = 0,01;" ? (graças a github.com/JuliaLang/julia/issues/4156#issuecomment-23324163 )
4esn0k
Na verdade, @Samuel, com valores negativos, esse método retorna resultados corretos, ou pelo menos retorna os mesmos valores que o método usando Math.trunc:). Eu verifiquei com 100,3; -100,3; 100, -3 e -100, -3. É claro que passou muito tempo desde que seu comentário e as coisas mudaram.
Marjan Venema
19

Eu normalmente uso:

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

Provavelmente não é o mais elegante, mas funciona.

Wolf Elkan
fonte
1
Solução agradável porque evita a feiúra de analisar ou truncar um carro alegórico.
Dem Pilafian
6
se você precisar do quociente e do restante, calcule o restante primeiro e depois reutilize esse valor na expressão do quociente, ou seja, quociente = (a - restante) / b;
GB96
2
restante = a% b; quociente = (a - restante) / b;
Zv_oDD 30/10/19
16

Você pode usar a função parseIntpara obter um resultado truncado.

parseInt(a/b)

Para obter um restante, use o operador mod:

a%b

parseInt tem algumas armadilhas com cordas, para evitar o uso do parâmetro radix com base 10

parseInt("09", 10)

Em alguns casos, a representação em cadeia do número pode ser uma notação científica; nesse caso, parseInt produzirá um resultado errado.

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

Esta chamada produzirá 1 como resultado.

Édipo Costa Rebouças
fonte
7
parseIntdeve ser evitado quando possível. Aqui está o aviso de Douglas Crockford: "Se o primeiro caractere da string for 0, a string será avaliada na base 8 em vez da base 10. Na base 8, 8 e 9 não são dígitos, então parseInt (" 08 ") e parseInt ("09") produzem 0 como resultado. Esse erro causa problemas em programas que analisam datas e horas. Felizmente, parseInt pode usar um parâmetro radix, para que parseInt ("08", 10) produz 8. Recomendamos que você sempre forneça o parâmetro radix ". archive.oreilly.com/pub/a/javascript/excerpts/…
Powers
3
Em uma divisão, espero receber um número, não uma string, mas esse é um bom argumento.
Édipo Costa Rebouças
2
@ Powers, adicione o radix. Ele não diz que parseIntdeve ser evitado; apenas que existem algumas dicas para estar ciente. Você deve estar ciente dessas coisas e estar preparado para lidar com isso.
Nenhum
2
Nunca ligue parseIntcom um argumento numérico. parseIntdeve analisar seqüências parcialmente numéricas, não truncar números.
Oriol
1
Só porque as coisas não foram originalmente concebidas para serem usadas de uma certa maneira, isso não significa que você não deva. Esta resposta funciona.
22717 fregante
6

O JavaScript calcula exatamente o piso dos números negativos e o restante dos números não inteiros, seguindo as definições matemáticas para eles.

FLOOR é definido como "o maior número inteiro menor que o parâmetro", portanto:

  • números positivos: FLOOR (X) = parte inteira de X;
  • números negativos: FLOOR (X) = parte inteira de X menos 1 (porque deve ser MENOR que o parâmetro, ou seja, mais negativo!)

REMAINDER é definido como o "restante" de uma divisão (aritmética euclidiana). Quando o dividendo não é um número inteiro, o quociente geralmente também não é um número inteiro, ou seja, não há resto, mas se o quociente for forçado a ser um número inteiro (e é isso que acontece quando alguém tenta obter o restante ou módulo de um número de ponto flutuante), haverá um não inteiro "sobra", obviamente.

O JavaScript calcula tudo conforme o esperado, portanto, o programador deve ter o cuidado de fazer as perguntas apropriadas (e as pessoas devem ter o cuidado de responder ao que é solicitado!). A primeira pergunta de Yarin NÃO foi "qual é a divisão inteira de X por Y", mas, em vez disso, "o número inteiro de vezes que um determinado número inteiro entra em outro". Para números positivos, a resposta é a mesma para ambos, mas não para números negativos, porque a divisão inteira (dividendo por divisor) será -1 menor que as vezes que um número (divisor) "entra" em outro (dividendo). Em outras palavras, FLOOR retornará a resposta correta para uma divisão inteira de um número negativo, mas Yarin não perguntou isso!

gammax respondeu corretamente, esse código funciona conforme solicitado por Yarin. Por outro lado, Samuel está errado, ele não fez as contas, eu acho, ou ele teria visto que funciona (também, ele não disse qual era o divisor de seu exemplo, mas espero que tenha sido 3):

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

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

A propósito, testei o código no Firefox 27.0.1, ele funcionou conforme o esperado, com números positivos e negativos e também com valores não inteiros, tanto para dividendos quanto para divisores. Exemplo:

-100.34 / 3.57: GoesInto = -28, Restante = -0.3800000000000079

Sim, notei que existe um problema de precisão, mas não tive tempo de verificá-lo (não sei se é um problema com o Firefox, Windows 7 ou com a FPU da minha CPU). Para a pergunta de Yarin, porém, que envolve apenas números inteiros, o código da gammax funciona perfeitamente.

Cyberknight
fonte
5

Math.floor(operation) retorna o valor arredondado da operação.

Exemplo de uma r questão:

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

console.log(z);

Console:

15

Exemplo da questão:

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

console.log(x);

Console:

4

Aetricity
fonte
2

O cálculo do número de páginas pode ser feito em uma etapa: Math.ceil (x / y)

user1920925
fonte
Não vejo como isso fornece o restante.
Paul Rooney
1

O comentário de Alex Moore-Niemi como resposta:

Para Rubyists aqui do Google em busca de divmod, você pode implementá-lo da seguinte forma:

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

Resultado:

// [2, 33]
Alex
fonte
2
Geralmente, divmodusa divisão com piso ( Math.floor), que difere da divisão truncada ( Math.trunc) quando números negativos estão envolvidos. Este é o caso do pacote NPMdivmod , Rubydivmod , SWI-Prologdivmod e provavelmente muitas outras implementações também.
Palec 7/07
A divisão truncada fornece resultados com aparência mais natural do que a divisão com piso, mas a compatibilidade supera isso, IMO. Talvez também haja razões matemáticas ou de desempenho para o uso de divisão de piso. Observe que geralmente divmodexiste porque é executado duas vezes mais rápido que o cálculo das duas operações separadamente. Fornecer essa função sem esse benefício de desempenho pode ser confuso.
Palec 07/07
1

Se você está apenas dividindo com potências de dois, pode 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];
}

(O primeiro é o quociente, o segundo o restante)

Konstantin Möllers
fonte
Geralmente function divideByPowerOf2(num, exponent) { return [num >> exponent, num & ((1 << exponent) - 1)]; }.
Palec 13/09/17
0

Você pode usar o ternário para decidir como lidar com valores inteiros positivos e negativos.

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

Se o número for positivo, está tudo bem. Se o número for negativo, ele adicionará 1 por causa de como o Math.floor lida com negativos.

John Galt
fonte
0

Isso sempre truncará para zero. Não tenho certeza se é tarde demais, mas aqui vai:

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
fonte
0

Se você precisar calcular o restante para números inteiros muito grandes, os quais o tempo de execução JS não pode representar como tal (qualquer número inteiro maior que 2 ^ 32 é representado como float e, portanto, perde precisão), é necessário fazer algum truque.

Isso é especialmente importante para verificar muitos casos de dígitos de cheque que estão presentes em muitos casos de nossa vida diária (números de contas bancárias, cartões de crédito, ...)

Antes de tudo, você precisa do seu número como uma string (caso contrário, você já perdeu a precisão e o restante não faz sentido).

str = '123456789123456789123456789'

Agora você precisa dividir sua corda em partes menores, pequenas o suficiente para que a concatenação de qualquer restante e um pedaço de corda possa caber em 9 dígitos.

digits = 9 - String(divisor).length

Prepare uma expressão regular para dividir a sequência

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

Por exemplo, se digitsfor 7, o regexp é

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

Corresponde a uma substring não vazia de comprimento máximo 7, que é seguida ((?=...) por um número de caracteres múltiplo de 7. O 'g' é para fazer a expressão percorrer toda a cadeia, sem parar na primeira correspondência.

Agora converta cada parte em número inteiro e calcule os demais reduce(adicionando novamente o restante anterior - ou 0 - multiplicado pela potência correta de 10):

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

Isso funcionará devido ao algoritmo restante "subtração":

n mod d = (n - kd) mod d

que permite substituir qualquer 'parte inicial' da representação decimal de um número pelo restante, sem afetar o restante final.

O código final seria semelhante 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);
}
reescrito
fonte