Jak java wykonuje obliczenia modułu z liczbami ujemnymi?


96

Czy robię źle moduł? Ponieważ w Javie -13 % 64ma oceniać, -13ale dostaję 51.


102
@Dan Nawet bez statycznej głównej pustki ... Widzę kod.
Joris Meys

22
Dostaję -13 i 64 == -13
Grodriguez

7
Jak to jest, że dostajesz 51 ratehr niż -13.
Raedwald

3
W ogóle nie robisz modułu. W Javie nie ma operatora modulo. %jest operatorem reszty.
Markiz Lorne,

3
Mylące pytanie: Java 8 daje -13, jak niektórzy mówią. Z jaką wersją Java podobno to otrzymałeś?
Jan Żankowski

Odpowiedzi:


105

W użyciu są obie definicje modułu liczb ujemnych - niektóre języki używają jednej definicji, a inne drugiej.

Jeśli chcesz uzyskać liczbę ujemną dla ujemnych danych wejściowych, możesz użyć tego:

int r = x % n;
if (r > 0 && x < 0)
{
    r -= n;
}

Podobnie, jeśli używasz języka, który zwraca liczbę ujemną na ujemnych danych wejściowych, a wolałbyś dodatnie:

int r = x % n;
if (r < 0)
{
    r += n;
}

3
To nie działa dobrze, jeśli n jest ujemne. Jeśli używasz tego samego przykładu ze specyfikacji języka Java 7 (sekcja 15.17.3): (-5)% (-3) = -2. Dodanie -3 nie zadziała. Należy dodać wartość bezwzględną n, jeśli chcesz mieć pewność, że jest ona dodatnia.
partlov

6
W Javie negatywne modulo niczego nie zmienia, jeśli i tak używasz Abs (), po prostu napisz r = x% abs (n). Nie podoba mi się instrukcja if, wolę raczej napisać r = ((x% n) + n)% n. Jeśli chodzi o moc 2 modulo (2,4,8,16 itd.) I odpowiedź pozytywną, rozważ maskę binarną r = x & 63.
Fabyen

3
W kontekście Javy (jak w tagu pytania) ta odpowiedź jest zasadniczo „zła”. Biorąc pod uwagę wyrażenie x % y, A) jeśli xjest ujemne, reszta jest ujemna, tj x % y == -(-x % y). B) znak ynie ma efektu, tj.x % y == x % -y
Bohemian

72

Ponieważ „matematycznie” oba są poprawne:

-13 % 64 = -13 (on modulus 64)  
-13 % 64 = 51 (on modulus 64)

Jedna z opcji musiała zostać wybrana przez programistów języka Java i wybrali:

znak wyniku jest równy znakowi dywidendy.

Mówi to w specyfikacji Java:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.3


8
Pytanie brzmi "dlaczego Java daje mi, -13 % 64 = 51kiedy się spodziewałem -13?".
Pascal Cuoq,

5
@pascal: java podaje właściwą definicję w matematyce i sposób, w jaki została zaimplementowana, aby nie robić tego, czego od niej oczekujesz.

9
Matematycznie rozsądne zachowanie jest dostępne w Javie 8: Math.floorMod
Jesse Glick

1
Coś w ich 15.17.3. Pozostałe przykłady operatora% nie są jasne. int result = (-5) % 3;daje -2. int result = (-3) % 5;daje -3. Ogólnie int result = (-a) % b;daje właściwą odpowiedź, gdy | -a | > b. Aby uzyskać poprawny wynik, gdy | -a | <b powinniśmy zawinąć dzielnik. int result = ((-a) % b) + b;dla negatywnych a lub int result = (((-a) % b) + b) % b;pozytywnych lub negatywnych a
Oz Edri

@Caner Nie rozumiem tego, jak oba mogą być takie same?
Kishore Kumar Korada

20

Czy na pewno pracujesz w Javie? ponieważ Java daje -13% 64 = -13 zgodnie z oczekiwaniami. Znak dywidendy!


15

Twój wynik jest nieprawidłowy dla języka Java. Podaj kontekst, w jaki sposób do tego doszedłeś (Twój program, implementacja i wersja Java).

Ze specyfikacji języka Java

15.17.3 Pozostały operator%
[...]
Operacja reszty dla operandów, które są liczbami całkowitymi po binarnej promocji liczbowej (§5.6.2) daje taką wartość wyniku, że (a / b) * b + (a% b) jest równe za.
15.17.2 Operator oddziału /
[...]
Dzielenie liczb całkowitych zaokrągla w kierunku 0.

Ponieważ / jest zaokrąglane w kierunku zera (co daje zero), wynik% powinien w tym przypadku być ujemny.


Coś w ich 15.17.3. Pozostałe przykłady operatora% nie są jasne. int result = (-5) % 3;daje -2 int result = (-3) % 5;daje -3 Ogólnie int result = (-a) % b;daje poprawną odpowiedź, gdy | -a | > b Aby uzyskać poprawny wynik, gdy | -a | <b powinniśmy zawinąć dzielnik. int result = ((-a) % b) + b;za negatywne a lub int result = (((-a) % b) + b) % b;pozytywne lub negatywne a.
Oz Edri

1
Twój komentarz jest dość niejasny. Ta sekcja definiuje poprawny wynik, a przykłady zgadzają się z tą definicją. Na przykład (-3) % 5poprawny wynik zgodnie z definicją to -3, a poprawna implementacja Javy powinna dać ten wynik.
starblue

Chyba nie wyjaśniłem się poprawnie. Przez „właściwą odpowiedź” miałem na myśli, gdy | -a | <b, aby otrzymać wynik pozytywny, powinniśmy „zawinąć” dany wynik z a% b, dodając do niego b. W moim przykładzie (-3)%5rzeczywiście daje -3, a jeśli chcemy dodatniej reszty, powinniśmy dodać do niej 5, a wtedy wynik będzie2
Oz Edri

6

możesz użyć

(x % n) - (x < 0 ? n : 0);

3
@ruslik Można również zrobić: ((x % k) + k) % k. (Chociaż twój jest prawdopodobnie bardziej czytelny.)
John Kurlak

2
@JohnKurlak Twoja wersja działa tak: 4% 3 = 1 LUB 4% -3 = -2 LUB -4% 3 = 2 LUB -4% -3 = -1 ale ta od Ruslika działa tak: 4% 3 = 1 LUB 4% -3 = 1 LUB -4% 3 = -4 LUB -4% -3 = 2
Joschua

1
@Joschua Dzięki za wskazanie tego. Mój kod jest pomocny, gdy chcesz, aby wynik modułu znajdował się w zakresie [0, sign(divisor) * divisor)zamiast [0, sign(dividend) * divisor).
John Kurlak

3

Twoja odpowiedź jest w Wikipedii: operacja modulo

Mówi, że w Javie znak operacji modulo jest taki sam jak w przypadku dywidendy. a ponieważ mówimy o pozostałej części operacji dzielenia jest w porządku, zwraca ona w twoim przypadku -13, ponieważ -13/64 = 0 -13-0 = -13.

EDYCJA: Przepraszamy, źle zrozumiałem Twoje pytanie ... Masz rację, java powinien dać -13. Czy możesz podać więcej kodu otaczającego?


2

Arytmetyka modulo z ujemnymi operandami jest definiowana przez projektanta języka, który może pozostawić ją implementacji języka, który może odłożyć definicję do architektury procesora.

Nie mogłem znaleźć definicji języka Java.
Dzięki Ishtar, specyfikacja języka Java dla operatora reszty% mówi, że znak wyniku jest taki sam jak znak licznika.


1

Aby temu zaradzić, możesz dodać 64(lub jakąkolwiek twoją podstawę modułu) do wartości ujemnej, aż będzie dodatnia

int k = -13;
int modbase = 64;

while (k < 0) {
    k += modbase;
}

int result = k % modbase;

Wynik będzie nadal w tej samej klasie równoważności.


1

x = x + m = x - mw module m.
tak -13 = -13 + 64w module 64 i -13 = 51w module 64.
załóżmy Z = X * d + r, że jeśli 0 < r < Xto w dzieleniu Z/Xnazywamy rresztę.
Z % Xzwraca pozostałą część Z/X.


1

Funkcja mod jest definiowana jako wartość, o którą liczba przekracza największą całkowitą wielokrotność dzielnika, która nie jest większa niż ta liczba. Więc w twoim przypadku

-13 % 64

największa całkowita wielokrotność liczby 64, która nie przekracza -13, to -64. Teraz, kiedy odejmiesz -13 od -64, to równa się 51-13 - (-64) = -13 + 64 = 51


0

W mojej wersji Java JDK 1.8.0_05 -13% 64 = -13

możesz spróbować -13- (int (-13/64)), innymi słowy wykonaj rzut dzielenia na liczbę całkowitą, aby pozbyć się części ułamkowej, a następnie odejmij od licznika Więc licznik- (int (licznik / mianownik)) powinien dać poprawną pozostała i podpisz



0

Zgodnie z sekcją 15.17.3 JLS, „Pozostała operacja dla operandów, które są liczbami całkowitymi po binarnej promocji liczbowej, daje taką wartość wyniku, że (a / b) * b + (a% b) jest równe a. Ta tożsamość zachowuje nawet w szczególnym przypadku, gdy dywidenda jest ujemną liczbą całkowitą o największej możliwej wielkości dla swojego typu, a dzielnik wynosi -1 (reszta to 0). "

Mam nadzieję, że to pomoże.


-1

Myślę, że Java nie zwraca w tym przypadku 51. Używam Java 8 na komputerze Mac i otrzymuję:

-13 % 64 = -13

Program:

public class Test {
    public static void main(String[] args) {
        int i = -13;
        int j = 64;
        System.out.println(i % j);
    }
}

5
@XaverKapeller, nie! Wiele osób zwróciło uwagę, że z matematycznego punktu widzenia -13 i 51 są poprawne. W Javie oczekiwana jest odpowiedź -13 i ja też ją otrzymałem, więc nie wiem, skąd przesyłający uzyskał 51, to tajemnica. Szczegóły trybu dotyczące kontekstu mogą pomóc w prawidłowej odpowiedzi na to pytanie.
Fabyen

@Xaver Kapeller: Jak 51 i -13 mogą być poprawne? Java zwróciłaby tylko jedną wartość ..
ceprateek

@XaverKapeller W jaki sposób odpowiedź, która dokumentuje, co faktycznie robi Java, może być błędna?
Markiz Lorne

@EJP Myślę, że 3 lata temu, kiedy to pisałem, byłem na tyle głupi, że ceniłem dokładność matematyczną bardziej niż prostą rzeczywistość, jak radzi sobie z tym java. Dzięki za przypomnienie mi o usunięciu mojego głupiego komentarza: D
Xaver Kapeller
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.