Em java quando você faz
a % b
Se a for negativo, ele retornará um resultado negativo, em vez de voltar para b como deveria. Qual é a melhor maneira de consertar isso? A única maneira de pensar é
a < 0 ? b + a : a % b
Em java quando você faz
a % b
Se a for negativo, ele retornará um resultado negativo, em vez de voltar para b como deveria. Qual é a melhor maneira de consertar isso? A única maneira de pensar é
a < 0 ? b + a : a % b
Respostas:
Ele se comporta como deveria a% b = a - a / b * b; ou seja, é o restante.
Você pode fazer (a% b + b)% b
Esta expressão funciona como o resultado de (a % b)
é necessariamente menor que b
, não importa se a
é positivo ou negativo. Adicionar b
cuida dos valores negativos de a
, já que (a % b)
é um valor negativo entre -b
e 0
, (a % b + b)
é necessariamente menor que b
e positivo. O último módulo está lá caso a
fosse positivo para começar, pois se a
fosse positivo (a % b + b)
se tornaria maior que b
. Portanto, o (a % b + b) % b
transforma em menor do que b
novamente (e não afeta os a
valores negativos ).
(a % b)
é necessariamente menor que b
(não importa se a
é positivo ou negativo), a adição b
cuida dos valores negativos de a
, pois (a % b)
é menor que b
e menor que 0
, (a % b + b)
é necessariamente menor que b
e positivo. O último módulo está lá caso a
fosse positivo para começar, pois se a
fosse positivo (a % b + b)
se tornaria maior que b
. Portanto, o (a % b + b) % b
transforma em menor do que b
novamente (e não afeta os a
valores negativos ).
a < 0
, talvez você pudesse dar uma olhada)
(a % b + b) % b
divide para valores muito grandes de a
e b
. Por exemplo, usar a = Integer.MAX_VALUE - 1
e b = Integer.MAX_VALUE
dará -3
como resultado, que é um número negativo, que é o que você queria evitar.
while
seria mais lento se você realmente precisar, exceto que você só precisa de um if
, caso em que é realmente mais rápido.
A partir do Java 8, você pode usar Math.floorMod (int x, int y) e Math.floorMod (long x, long y) . Ambos os métodos retornam os mesmos resultados da resposta de Peter.
Math.floorMod( 2, 3) = 2
Math.floorMod(-2, 3) = 1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
float
ou double
argumentos. O operador binário mod ( %
) também funciona com operandos float
e double
.
Para aqueles que ainda não usam (ou não podem usar) o Java 8, o Guava veio ao resgate com IntMath.mod () , disponível desde o Guava 11.0.
IntMath.mod( 2, 3) = 2
IntMath.mod(-2, 3) = 1
Uma advertência: ao contrário do Math.floorMod () do Java 8, o divisor (o segundo parâmetro) não pode ser negativo.
Na teoria dos números, o resultado é sempre positivo. Eu diria que nem sempre é o caso em linguagens de computador, porque nem todos os programadores são matemáticos. Meus dois centavos, eu consideraria um defeito de design da linguagem, mas você não pode mudar isso agora.
= MOD (-4.180) = 176 = MOD (176, 180) = 176
porque 180 * (-1) + 176 = -4 o mesmo que 180 * 0 + 176 = 176
Usando o exemplo do relógio aqui, http://mathworld.wolfram.com/Congruence.html você não diria duration_of_time mod cycle_length é -45 minutos, você diria 15 minutos, embora ambas as respostas satisfaçam a equação básica.
-1
vez de, n-1
por exemplo) então faça isso.
O Java 8 tem Math.floorMod
, mas é muito lento (sua implementação tem várias divisões, multiplicações e uma condicional). É possível que a JVM tenha um stub otimizado intrínseco para ela, no entanto, o que a aceleraria significativamente.
A maneira mais rápida de fazer isso sem floorMod
é como algumas outras respostas aqui, mas sem ramificações condicionais e apenas uma %
operação lenta .
Supondo que n seja positivo, e x pode ser qualquer coisa:
int remainder = (x % n); // may be negative if x is negative
//if remainder is negative, adds n, otherwise adds 0
return ((remainder >> 31) & n) + remainder;
Os resultados quando n = 3
:
x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
0| 0
1| 1
2| 2
3| 0
4| 1
Se você só precisa de uma distribuição uniforme entre 0
e n-1
e não o operador mod exato, e seu x
não agrupa próximo 0
, o seguinte será ainda mais rápido, pois há mais paralelismo de nível de instrução e o %
cálculo lento ocorrerá em paralelo com o outro partes, pois não dependem de seu resultado.
return ((x >> 31) & (n - 1)) + (x % n)
Os resultados para o acima com n = 3
:
x | result
----------
-5| 0
-4| 1
-3| 2
-2| 0
-1| 1
0| 0
1| 1
2| 2
3| 0
4| 1
5| 2
Se a entrada for aleatória em todo o intervalo de um int, a distribuição das duas soluções será a mesma. Se os clusters de entrada próximos de zero, haverá poucos resultados na n - 1
última solução.
Aqui está uma alternativa:
a < 0 ? b-1 - (-a-1) % b : a % b
Isso pode ou não ser mais rápido do que a outra fórmula [(a% b + b)% b]. Ao contrário da outra fórmula, ela contém uma ramificação, mas usa uma operação de módulo a menos. Provavelmente uma vitória se o computador puder prever um <0 corretamente.
(Editar: corrigida a fórmula.)