Деление и взятие остатка от отрицательного числа
От: SergH Россия  
Дата: 31.08.11 07:58
Оценка:
Привет!

Код:
    int a = -1;
    int b = 1000;

    std::cout << "a \\ b = " << (a / b) << ", a % b = " << (a % b) << "\n";


Результат:
a \ b = 0, a % b = -1


Это так и должно быть? Мне казалось, обычно принимается соглашение, что остаток всегда неотрицателен, соответственно получилось бы
a \ b = -1, a % b = 999


Именно так работает, например, Питон.

Я очень удивился, когда понял, что С++ работает не так. Это стандартное поведение или вывих компилятора (gcc 4.5.2)?
Делай что должно, и будь что будет
Re: Деление и взятие остатка от отрицательного числа
От: SergH Россия  
Дата: 31.08.11 08:01
Оценка:
Здравствуйте, SergH, Вы писали:

SH>Код:

SH>
SH>    int a = -1;
SH>    int b = 1000;

SH>    std::cout << "a \\ b = " << (a / b) << ", a % b = " << (a % b) << "\n";
SH>


Вот интересно, почему в строке я использовал \, а не /? Но на результат операции деления это не влияет, к сожалению.
Делай что должно, и будь что будет
Re: Деление и взятие остатка от отрицательного числа
От: Alexander Poluektov Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 31.08.11 08:08
Оценка: 18 (1)
Здравствуйте, SergH, Вы писали:

SH>Привет!


SH>Результат:

SH>
SH>a \ b = 0, a % b = -1
SH>


SH>Это так и должно быть? Мне казалось, обычно принимается соглашение, что остаток всегда неотрицателен, соответственно получилось бы

SH>
SH>a \ b = -1, a % b = 999
SH>


SH>Это стандартное поведение или вывих компилятора (gcc 4.5.2)?


В случае, когда любой из аргументов целочисленного деления или взятия остатка отрицательный, компилятор может "округлять" в любую сторону, и, соответственно, возвращать положительный или отрицательный остаток.

Соответственно, в данном результат может быть как -1, так и 999.

Ссылку на стандарт сейчас не найду.
Re[2]: Деление и взятие остатка от отрицательного числа
От: SergH Россия  
Дата: 31.08.11 08:13
Оценка:
Здравствуйте, Alexander Poluektov, Вы писали:

AP>В случае, когда любой из аргументов целочисленного деления или взятия остатка отрицательный, компилятор может "округлять" в любую сторону, и, соответственно, возвращать положительный или отрицательный остаток.


AP>Соответственно, в данном результат может быть как -1, так и 999.


AP>Ссылку на стандарт сейчас не найду.


Ясно, спасибо. Хотя и очень странно, на мой взгляд -- простейшая арифметическая операция, а результат непредсказуем.
Делай что должно, и будь что будет
Re[2]: Деление и взятие остатка от отрицательного числа
От: Alexander Poluektov Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 31.08.11 08:14
Оценка: 9 (1)
Здравствуйте, Alexander Poluektov, Вы писали:

SH>>Это стандартное поведение или вывих компилятора (gcc 4.5.2)?


AP>В случае, когда любой из аргументов целочисленного деления или взятия остатка отрицательный, компилятор может "округлять" в любую сторону, и, соответственно, возвращать положительный или отрицательный остаток.


AP>Соответственно, в данном результат может быть как -1, так и 999.


AP>Ссылку на стандарт сейчас не найду.


А впрочем, вот и она

5.6 Multiplicative operators, section 4

The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined; otherwise (a/b)*b + a%b is equal to a. If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined.


И сноска:

According to work underway toward the revision of ISO C, the preferred algorithm for integer division follows the rules defined in the ISO Fortran standard, ISO/IEC 1539:1991, in which the quotient is always rounded toward zero.


Стандарт, правда, старый, 2003 года. Но, думаю, такие вещи они никогда не поменяют.
Re: Деление и взятие остатка от отрицательного числа
От: stapter  
Дата: 31.08.11 08:25
Оценка:
Здравствуйте, SergH, Вы писали:

SH>Привет!


SH>Код:

SH>
SH>    int a = -1;
SH>    int b = 1000;

SH>    std::cout << "a \\ b = " << (a / b) << ", a % b = " << (a % b) << "\n";
SH>


SH>Результат:

SH>
SH>a \ b = 0, a % b = -1
SH>


SH>Это так и должно быть? Мне казалось, обычно принимается соглашение, что остаток всегда неотрицателен, соответственно получилось бы

SH>
SH>a \ b = -1, a % b = 999
SH>


SH>Именно так работает, например, Питон.


SH>Я очень удивился, когда понял, что С++ работает не так. Это стандартное поведение или вывих компилятора (gcc 4.5.2)?


Соглашения о неотрицательном остатке в с++ нет (по крайней мере я о таком ни разу не слышал).
И более того мне такой подход больше нравится, с точки зрения математики получается все довольно просто и определенно.
А в случае соглашения с неотрицательным остатком, получается куча представлений остатков:
a \ b = -1, a % b = 999
a \ b = -2, a % b = 1999
a \ b = -3, a % b = 2999
и так далее...
Все эти записи получаются эквивалентными.
Конечно в случае с питоном, наверняка, накладываться ограничение на max(a\b), чтобы не возникало данное множество эквивалентных представлений. Но, на первый взгляд, данное соглашение добавляет путаницы. Интересно узнать причины его появления, уверен, что оно появилось не просто так.
Re[3]: Деление и взятие остатка от отрицательного числа
От: Alexey F  
Дата: 31.08.11 08:28
Оценка: 23 (3)
Здравствуйте, Alexander Poluektov, Вы писали:

AP>

AP>5.6 Multiplicative operators, section 4

AP>The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined; otherwise (a/b)*b + a%b is equal to a. If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined.


AP>Стандарт, правда, старый, 2003 года. Но, думаю, такие вещи они никогда не поменяют.


Поменяли. Строка про implementation-defined убрана:

5.6.4 The binary / operator yields the quotient, and the binary % operator yields the remainder from the division
of the first expression by the second. If the second operand of / or % is zero the behavior is undefined. For
integral operands the / operator yields the algebraic quotient with any fractional part discarded;[сноска 82: This is often called truncation towards zero]
if the quotient a/b is representable in the type of the result, (a/b)*b+a%b is equal to a.

Re[2]: Деление и взятие остатка от отрицательного числа
От: SergH Россия  
Дата: 31.08.11 08:33
Оценка:
Здравствуйте, stapter, Вы писали:

S>Соглашения о неотрицательном остатке в с++ нет (по крайней мере я о таком ни разу не слышал).

S>И более того мне такой подход больше нравится, с точки зрения математики получается все довольно просто и определенно.
S>А в случае соглашения с неотрицательным остатком, получается куча представлений остатков:
S>a \ b = -1, a % b = 999
S>a \ b = -2, a % b = 1999
S>a \ b = -3, a % b = 2999
S>и так далее...
S>Все эти записи получаются эквивалентными.

Обычно на остаток накладывается ещё одно ограничение Он не только неотрицателен, но и меньше делителя. Если он больше делителя, это уже не остаток, а что-то другое. Альтернативный вариант, предложенный мне gcc -- когда остаток по модулю меньше делителя. Как раз C++ может выдать оба варианта

-1 / 1000 = 0, -1 % 1000 = -1
-1 / 1000 = -1, -1 % 1000 = 999


а я предлагаю остановиться на втором. Мне кажется, неопределенное поведение компилятора это всегда зло, так как на него не может полагаться ни тот, кто хотел первое, ни тот, кто хотел второе, оба будут вынуждены проверять.

S>Конечно в случае с питоном, наверняка, накладываться ограничение на max(a\b), чтобы не возникало данное множество эквивалентных представлений. Но, на первый взгляд, данное соглашение добавляет путаницы. Интересно узнать причины его появления, уверен, что оно появилось не просто так.


Ммм, тебе что-нибудь говорит словосочетание "классы вычетов по модулю"?
Делай что должно, и будь что будет
Re[4]: Деление и взятие остатка от отрицательного числа
От: Alexander Poluektov Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 31.08.11 08:33
Оценка: :)
Здравствуйте, Alexey F, Вы писали:

AP>>Стандарт, правда, старый, 2003 года. Но, думаю, такие вещи они никогда не поменяют.


AF>Поменяли. Строка про implementation-defined убрана:

AF>

AF>5.6.4 The binary / operator yields the quotient, and the binary % operator yields the remainder from the division
AF>of the first expression by the second. If the second operand of / or % is zero the behavior is undefined. For
AF>integral operands the / operator yields the algebraic quotient with any fractional part discarded;[сноска 82: This is often called truncation towards zero]
AF>if the quotient a/b is representable in the type of the result, (a/b)*b+a%b is equal to a.


Охренеть!
Re[4]: Деление и взятие остатка от отрицательного числа
От: se_sss  
Дата: 31.08.11 09:05
Оценка:
AF>Поменяли. Строка про implementation-defined убрана:
AF>[/q]
Это, конечно, хорошо.
Только в ближайшие годы полагаться на это всё же не стоит.
Неизвестно, каким компилятором понадобится компилировать.
Re[3]: Деление и взятие остатка от отрицательного числа
От: stapter  
Дата: 31.08.11 10:50
Оценка:
Здравствуйте, SergH, Вы писали:

SH>Мне кажется, неопределенное поведение компилятора это всегда зло, так как на него не может полагаться ни тот, кто хотел первое, ни тот, кто хотел второе, оба будут вынуждены проверять.


полностью согласен, в данном случае, о двойном поведении компилятора не знал.

SH>Ммм, тебе что-нибудь говорит словосочетание "классы вычетов по модулю"?

Могу приврать, но насколько помню, это множество чисел с одинаковым остатком от деления на "модуль".
Re[4]: Деление и взятие остатка от отрицательного числа
От: stapter  
Дата: 31.08.11 10:51
Оценка:
Здравствуйте, stapter, Вы писали:

S>полностью согласен, в данном случае, о двойном поведении компилятора не знал.


правильнее будет сказать implementation-defined
Re[4]: Деление и взятие остатка от отрицательного числа
От: SergH Россия  
Дата: 31.08.11 11:19
Оценка:
Здравствуйте, stapter, Вы писали:

SH>>Ммм, тебе что-нибудь говорит словосочетание "классы вычетов по модулю"?

S>Могу приврать, но насколько помню, это множество чисел с одинаковым остатком от деления на "модуль".

ну, приблизительно, да.

только, извини, но кажется я забыл, какая дальше была мысль чёрт, а такое начало умное было.

кажется я имел ввиду, что здорово, когда просто взятим остатка можно что-то понять про число. Типа того, что (i % 2 == 1) -- оно нечётное. Оказывается это неправильный код, он не работает с отрицательными числами. Нужно либо вместо проверки на нечётность ставить проверку на чётность, либо проверять ещё -1. Хотя на первый взгляд не заподозришь. То есть появляются два случая. И для того, чтобы понять, что у двух чисел одинаковые модули вместо тупого сравнения остатков (которое оказывается не просто более медленным, а некорректным!) нужно писать что-то типа ((i — j) % n == 0).

Зачем мне для этого понадобились классы вычетов... Ну может так: у каждого класса вычетов есть канонический представитель, один, и обычно это число из диапазона [0, N-1]. И я подумал, что было бы прекрасно, если бы результат % совпадал с этим представителем.
Делай что должно, и будь что будет
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.