Я был удивлен, когда этот цикл for
не работал должным образом:
for (std::size_t i=2; i >= 0; --i)
Я подумал, ладно, возможно, последняя проверка — это если -1 >= 0
, а поскольку i
не может быть отрицательным, у нас проблема. Предположительно i
зацикливается на (264 - 1).
Однако этот цикл for
выполняется:
for (std::size_t i=2; i+1 > 0; --i)
Ненадолго игнорируя std::size_t
; это не имеет смысла для меня с логической точки зрения. И (i+1 > 0)
, и (i >= 0)
будут либо истинными, либо ложными для одних и тех же значений i
.
Оба будут истинными, если i = {0, 1, 2, ...}
, и ложными, если i = {-1, -2, -3, ...}
.
Что здесь происходит?
Это как-то связано с реализацией std::size_t
или компилятором, или я просто упускаю что-то очень очевидное?
i
(то есть неподписанный) не может быть ни одним из этих значений, потому что все эти значения отрицательные.
Этот вопрос может потребовать большей ясности. Вы упоминаете, что «я не может быть отрицательным», а затем описываете «если я = {-1, -2, -3, ...}». Разве эти утверждения не противоречат друг другу? Что значит "не удалось запустить должным образом"?
size_t
— беззнаковый тип. В таких типах отрицательных значений не существует, но 0-1 определяется как значение, к которому добавление 1 даст 0 — максимальное представимое целочисленное значение size_t
.
Так да:
I figured, okay, probably the final check is if -1 >= 0, and since i is not allowed to be negative, we got a problem. Presumably i is looping around to 2^64 - 1.
точно.
for (std::size_t i=2; i+1 > 0; --i)
Итак, i
достигает 2⁶⁴-1, и вы добавляете 1, так что вы получаете 0
, что равно нет>0
, и ваш цикл завершается.
Здесь все в порядке!
What is going on here?
std::size_t
— целочисленный тип без знака.
i >= 0
Все беззнаковые целые числа больше или равны 0. Не существует значения, для которого это отношение было бы ложным, и, следовательно, цикл не может закончиться.
i+1 > 0
Целое число без знака могу равно 0. Следовательно, это отношение может быть ложным, и цикл может закончиться. Пример:
std::size_t i = 0;
i -= 1;
assert(i+1 == 0);
Значение i
, которое завершает цикл, соответствует -1 по модулю M, где M — количество представляемых значений, равное 2b, где b — ширина целочисленного типа в битах. Это число является наибольшим представимым значением, то есть 2b-1.
Ваш вывод работает с целыми числами, но не с модульной арифметикой.
В какой-то степени это дело вкуса, но я рекомендую следующий код для циклического перебора чисел (n..0]. Он корректно работает как с подписанными, так и с беззнаковыми типами:
for (std::size_t i=n; i-- > 0;)
Ах да, неуловимый оператор C стрелкаi --> 0
!
@chux-ReinstateMonica На этот раз это замаскировано;)
Вам просто нужно применить те же рассуждения ко второму примеру, что и ко второму.
-1 + 1
равно0
независимо от того, находится ли оно вsigned
илиunsigned
целочисленной арифметике.