Подтвердить что ты не робот

Является ли numeric_limits <int>:: is_modulo логически противоречивым?

В еще один вопрос появилась тема std::numeric_limits<int>::is_modulo. Но чем больше я думаю об этом, тем больше кажется, что что-то не так с spec или с GCC или обоими.

Позвольте мне начать с кода:

#include <limits>
#include <iostream>

bool test(int x)
{
    return x+1 > x;
}

int main(int argc, char *argv[])
{
    int big = std::numeric_limits<int>::max();

    std::cout << std::numeric_limits<int>::is_modulo << " ";
    std::cout << big+1 << " ";
    std::cout << test(big) << "\n";
}

Когда я компилирую это с помощью g++ -O3 -std=c++11 (x86_64 GCC 4.7.2), он производит следующий вывод:

1 -2147483648 1

То есть, is_modulo истинно, один плюс INT_MAX отрицателен, а один плюс INT_MAX больше, чем INT_MAX.

Если вы такой человек, у которого есть реальный шанс ответить на этот вопрос, вы уже знаете, что здесь произошло. В спецификации С++ указано, что переполнение целых чисел Undefined Behavior; компилятор допускает, что вы этого не делаете; поэтому аргумент x+1 не может быть INT_MAX; поэтому компилятор может (и будет) компилировать функцию test для возврата true безоговорочно. Пока что так хорошо.

Однако С++ 11 spec также говорит (18.3.2.4 абзацы 60-61):

static constexpr is_modulo;

Истинно, если тип по модулю .222 Тип по модулю, если для любой операции с участием +, - или * по значениям этого типа, результат которых выходят за пределы диапазона [min(),max()], возвращаемое значение отличается от истинного значения целым числом, кратным max() - min() + 1.

На большинстве машин это false для плавающих типов, true для целые числа без знака и true для целых чисел со знаком.

Обратите внимание, что в параграфе (5) раздела 5 все еще читается: "Если во время оценки выражения результат не определен математически или нет в диапазоне представляемых значений для его типа, поведение undefined". Не упоминается is_modulo == true создание исключения.

Таким образом, мне кажется, что стандарт логически противоречив, потому что одновременное переполнение целых чисел не может быть определено и undefined. Или, по крайней мере, GCC является несоответствующим, потому что он имеет is_modulo как true, хотя подписанная арифметика, безусловно, не оборачивается.

Является ли стандартная ошибка? Не соответствует ли GCC? Я что-то пропустил?

4b9b3361

Ответ 1

Если is_modulo является true для подписанного типа (например, int), который не изменяется обычными арифметическими преобразованиями, то для любой арифметической операции, отличной от деления на ноль, имеется единственный правильный результат в (математическом), которые по модулю сопоставляются с одним значением в диапазоне типа, и поэтому реализация ограничена, чтобы вести себя так, как если бы фактический результат был истинным результатом по модулю диапазона типа. Поэтому, если реализация хочет сохранить арифметику переполнения как undefined, она должна установить is_modulo в false.

Это обсуждалось ad nauseam в списке рассылки gcc, а затем под PR 22200 с окончательным заключением, что значение is_modulo должно быть false для подписанных типов; изменение было сделано в libstdС++ в апреле этого года.

Обратите внимание, что в С++ 03 язык был значительно другим:

18.2.1.2 numeric_limits members [lib.numeric.limits.members]

56 - [...] Тип по модулю, если можно добавить два положительных числа и получить результат обертывается до третьего числа, которое меньше.

Учитывая, что при действии undefined все возможно, можно утверждать, что предыдущее поведение libstdС++ (имеющее is_modulo как true) было правильным и соответствовало поведению g++; предыдущие обсуждения связанного PR следует читать с учетом этого.