C i Python-różne zachowania operacji modulo ( % )

Odkryłem, że ta sama operacja mod daje różne wyniki w zależności od tego, jaki język jest używany.

W Pythonie:

-1 % 10

Produkuje 9

W C wytwarza -1 !

  1. który z nich jest właściwym modulo?
  2. Jak sprawić, by operacja mod W C była taka sama jak w Pythonie?
Author: Cristian Ciupitu, 2009-12-15

7 answers

  1. oba warianty są poprawne, jednak w matematyce (zwłaszcza teorii liczb) najczęściej stosuje się Modulo Pythona.
  2. W C, robisz ((n % M) + M) % M aby uzyskać taki sam wynik jak w Pythonie. Np. ((-1 % 10) + 10) % 10. Zauważ, jak to nadal działa dla dodatnich liczb całkowitych: ((17 % 10) + 10) % 10 == 17 % 10, a także dla obu wariantów implementacji C (dodatnia lub ujemna reszta).
 64
Author: Alex B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-15 14:25:49

Python ma operację modulo "true", podczas gdy C ma operacjęrest .

Ma bezpośredni związek z obsługą ujemnego podziału całkowitego, tzn. zaokrąglony w kierunku 0 lub minus nieskończony. Python zaokrągla się w kierunku minus infinite I C (99) w kierunku 0, ale w obu językach (n/m)*m + n%m == n, więc operator % musi kompensować we właściwym kierunku.

Ada jest bardziej wyrazista i ma oba, jako mod i rem.

 27
Author: fortran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-15 14:20:31

W C89/90 zachowanie operatora podziału i operatora reszty z ujemnymi operandami jest zdefiniowane przez implementację , co oznacza, że w zależności od implementacji można uzyskać oba zachowania. Wymagane jest tylko, aby operatory zgadzały się ze sobą: z a / b = q i a % b = r wynika a = b * q + r. Użyj twierdzeń statycznych w kodzie, aby sprawdzić zachowanie, jeśli opiera się ono krytycznie na wyniku.

W C99 zachowanie, które obserwujesz, stało się standardem.

W rzeczywistości, albo zachowania mają w sobie pewną logikę. Zachowanie Pythona implementuje operację true modulo. Zachowanie, które zaobserwowałeś to C, jest zgodne z zaokrągleniem w kierunku 0(jest to również zachowanie Fortran).

Jednym z powodów, dla których Zaokrąglanie w kierunku 0 jest preferowane w C, jest to, że raczej naturalne jest oczekiwanie, że wynik -a / b będzie taki sam jak -(a / b). W przypadku prawdziwego zachowania modulo, -1 % 10 ocenia się do 9, co oznacza, że -1 / 10 musi wynosić -1. To może być postrzegane jako raczej nienaturalne, ponieważ -(1 / 10) jest 0.

 15
Author: AnT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-15 15:03:14

Obie odpowiedzi są poprawne, ponieważ -1 modulo 10 jest taka sama jak 9 modulo 10.

r = (a mod m)
a = n*q + r

Możesz być pewien, że |r| < |n|, ale nie Jaka jest wartość r. Są 2 odpowiedzi, negatywne i pozytywne.


W C89, chociaż odpowiedź zawsze będzie poprawna, dokładna wartość operacji modulo (odnoszą się do niej jako pozostałej) jest niezdefiniowana, co oznacza, że może to być wynik ujemny lub wynik dodatni. W C99 wynik jest zdefiniowany.

Jeśli chcesz pozytywnej odpowiedzi, możesz po prostu dodać 10, jeśli uznasz, że Twoja odpowiedź jest negatywna.

Aby operator modulo działał tak samo we wszystkich językach, pamiętaj, że:

n mod M == (n + M) mod M

I ogólnie:

n mod M == (n + X * M) mod M
 4
Author: Brian R. Bondy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-15 15:03:17

Od Pythona 3.7 można również używać .remainder() od math wbudowany Moduł.

Python 3.7.0a0 (heads/master:f34c685020, May  8 2017, 15:35:30)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.remainder(-1, 10)
-1.0

From docs :

Dla skończonych X i skończonych niezerowych y, jest to różnica x - n*y, gdzie n jest najbliższą liczbą całkowitą do dokładnej wartości ilorazu x / y. Jeśli x / y jest dokładnie w połowie drogi między dwiema kolejnymi liczbami całkowitymi, to najbliższa parzysta liczba całkowita jest używana dla n. Reszta r = remainder(x, y) zawsze spełnia abs(r) <= 0.5 * abs(y).

Przypadki specjalne następują po IEEE 754: w szczególności, remainder(x, math.inf) jest x dla dowolnego skończonego x, oraz remainder(x, 0) i remainder(math.inf, x) raise ValueError dla dowolnego nie-NaN x. Jeśli wynik operacji pozostałej wynosi zero, to zero będzie miało taki sam znak jak x.

Na platformach wykorzystujących zmiennoprzecinkowy kod binarny IEEE 754 wynik tej operacji jest zawsze dokładnie reprezentowalny: nie ma błędu zaokrąglania.

 0
Author: vishes_shell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-08 15:27:39

Możemy użyć modułu dziesiętnego Pythona, aby zachowywać się jak C

from decimal import Decimal
Decimal('-1') % Decimal('10')

Reprezentacja dziesiętna

 0
Author: Hariom Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-20 13:27:39

Wykonywanie dzielenia Euklidesowego a = b*q + r jest jak zaokrąglenie ułamka a/b do ilorazu całkowitego q, a następnie obliczenie reszty r.

Różne wyniki, które widzisz, zależą od konwencji używanej do zaokrąglania ilorazu...

Jeśli zaokrąglasz w kierunku zera (obcinasz), otrzymasz symetrię wokół zera, jak w C:

truncate(7/3) = 2
7 = 3*2 + 1

truncate(-7/3) = -2
-7 = 3* -2 - 1

truncate(7/-3) = -2
7 = -3* -2 + 1

Jeśli zaokrąglasz w kierunku ujemnej nieskończoności (podłogi), otrzymasz pozostałość jak w Pythonie:

floor(7/3) = 2
7 = 3*2 + 1

floor(-7/3) = -3
-7 = 3* -3 + 2

floor(7/-3) = -3
7 = -3* -3 - 2

Jeśli okrążysz do najbliższego int (wiązać do czego chcesz, do parzystości lub od zera) otrzymasz wyśrodkowany modulo:

round(7/3) = 2
7 = 3*2 + 1

round(8/3) = 3
8 = 3*3 - 1

round(-7/3) = -2
-7 = 3* -2 - 1

round(7/-3) = -2
7 = -3* -2 + 1
Możesz spróbować zaimplementować własne modulo z zaokrągleniem w kierunku dodatniej nieskończoności (ceil), i mógłbyś wymyślić raczej niekonwencjonalny modulo, ale nadal byłby to rodzaj modulo...
 0
Author: aka.nice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-20 22:54:56