Здравствуйте, T4r4sB, Вы писали:
ЕМ>> То, что подобные операции могут приводить к отрицательным смещениям, ничем не отличается от технической возможности обратиться к элементу с очень большим индексом
TB>Только на беззнаковом индексе хрен ты отличишь "попали раньше" от "попали позже".
А для чего это отличать? Один хрен попали неизвестно куда, вышли за пределы массива. Что нам даст это отличие?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Попадет в выражение вида a [ i ].
не попадет. Каждый раз перед обращением к выбранному должно проверяться — а есть ли выбранный. Это же естественно
ЕМ>А что значит "красивее логически"?
Потому что индекс может быть равен 65525. Ну чисто так, по логике. Может же? Может. Только вот это будет битый индекс.
А "-1" он не может быть равен никак. И это не зависит от размерности индекса, в смысле short, int или long. А вот если ты забьешь невалидное значение именно как max_unsigned_short, например, то при смене размерности массива до long — ты огребешь проблем. А при "-1"- нет
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Подобные рассуждения хороши для языков совсем уж высокого уровня, где разрядность значения всегда остается где-то на заднем плане. В C/C++ она является одним из ключевых признаков типа. Если программист недостаточно хорошо понимает особенности представления, C/C++ однозначно не для него.
Женя, я достаточно хорошо их понимаю, но мне откровенно не хочется заниматься анализом выражения на предмет того, где там может оказаться преобразование от int к unsigned int, которое, мягко говоря, выполняется способом, далеким от очевидности. Проще говоря, мне не хочется думать про -1, которая вдруг может оказаться 2^32-1. На эти грабли я натыкался и больше не хочу.
Чего ты добьешься, перейдя на unsigned ? Увеличения диапазона положительных чисел в 2 раза ? Смысла в этом немного, так как 2 миллиарда или 4 миллиарда — как правило, черт один, вполне хватит. А если не хватит, то надо на 64 бита переходить, а не бороться за один бит.
А больше я ничего не вижу. Зато я знаю, что если в результате арифметики с положительными числами результат оказался отрицательным там, где такого не должно быть,то ошибку надо искать в алгоритме, а не в неявных преобразованиях.
ЕМ>Даже во многих современных программах на C++ часто вижу int/short/long там, где по смыслу должно быть беззнаковое целое.
Дык нету в C/C++ беззнаковых целых, приходится пользоваться знаковыми. unsigned это не целые (ℤ), а арифметика по модулю. Название неудачное. Об этом ясно написано в ANSI C Rationale: RAT> The keyword unsigned is something of a misnomer (выделено мной), suggesting as it does arithmetic that is non-negative but capable of overflow. The semantics of the C type unsigned is that of modulus
ЕМ> А в ранних программах знаковые целые вообще использовались везде, где было технически возможно. Даже в классической книге Кернигана/Ритчи множество примеров, где счетчики, индексы и прочие имеют знаковый тип.
Раньше программирование не было такой массовой профессией с засильем "индусов" (в смысле интеллектуального уровня, а не национальности). Программистам было не западло RTFM и они знали, что слово "семантика" это не (только) ругательство. Поэтому там где нужны целые числа, использовали типы для целых чисел. А там, где нужна была семантика арифметики по модулю, использовали типы для арифметики по модулю — unsigned. Т.к. арифметика по модулю нужна крайне редко, поэтому неудивительно, что unsigned почти не встречался.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>А для чего это отличать? Один хрен попали неизвестно куда, вышли за пределы массива. Что нам даст это отличие?
Позволит отличить "уже" от "ещё".
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
H>Потому что индекс может быть равен 65525. Ну чисто так, по логике. Может же? Может. Только вот это будет битый индекс. H>А "-1" он не может быть равен никак. И это не зависит от размерности индекса, в смысле short, int или long. А вот если ты забьешь невалидное значение именно как max_unsigned_short, например, то при смене размерности массива до long — ты огребешь проблем. А при "-1"- нет
При вызове не будет никакой разницы. Оно упадет и так и эдак. В чем тут красота?
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>>А сколь часто такое встречается? Можно примеров?
TB>Да каждый раз, когда надо массив проитерировать наоборот.
Первая мысль, которая пришла в голову, после прочтения стартового сообщения — люди тупо не умеют писать циклы, перебирающие элементы в обратном порядке.
Поэтому юзают int.
Правда size() обычно таки возвращает size_t, что создает некоторые неудобства. Но не беда — мы его, ..., приведем к int:
for(int i=int(vec.size())-1;i>=0;--i)
{
}
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, T4r4sB, Вы писали:
TB>>Ты на беззнаках даже тупо от ЭН до нуля проитерироваться не можешь без дополнительного бубна.
R>А в чем проблема?
R>http://coliru.stacked-crooked.com/a/b2e320d32ba56367
R>
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Первая мысль, которая пришла в голову, после прочтения стартового сообщения — люди тупо не умеют писать циклы, перебирающие элементы в обратном порядке.
Тот факт, что их надо уметь писать специальным способом — это уже приговор.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, Alexander G, Вы писали:
AG>С точки зрение перформанса, есть случаи, где беззнаковые немного лучше, и нет случаев, где знаковые лучше.
Именно в C и C++ у знаковых принципиально лучше с точки зрения производительности, что позиция, что программист всегда позаботился о переполнениях, позволяет массу оптимизаций, типа замены a+1>a на true. Были отзывы о получении 20-50% выигрыша на этом в отдельных характерно интересных случаях.
Это хуже с точки зрения корректности (и потому я всегда говорю, что подобные оптимизации должны разрешаться только там, где программист это разрешил), иначе получаем проблемы типа такого
, но для производительности — таки в шоколаде.
AG> Примеры пользы беззнаковых: AG>- замена деления на степень двойки на сдвиг. Включая неявное деление в i < container.size()
Если компилятор отловил, что по факту там неотрицательное — может заменить и так.
Если нет — получается только 3-4 дополнительных команд на регистрах — ничто по сравнению с тормозами RAM.
AG>- fused операция при сравнении и переходе: while (i > 0) или for ( i ; i < 10 ; i++ ) CMP и JCC выполняются как одна операция только для беззнаковых.
Тоже копейки, которые вы сможете реализовать только пиша напрямую на ассемблере (иначе утонет в остальном сгенерированном коде).
AG>- обычные умножение/деление быстрее для беззнаковых
Что сравнивали-то? Intel рисует (почти) противоположное — для длинного умножения времена одинаковы, а для короткого (которому пофиг на знак) на 1-2 такта меньше (3 вместо до 5).
AG>С точки зрения корректности, знаковые как бы ограждают от одного типа ошибок (отрицательное число -> переполнение), но вносят другие (отрицательное число -> выход за диапазон), шило на мыло.
А у положительного переполнение у обоих. А так как мелкие числа, включая отрицательные, чаще, чем крупные, то знаковые числа заведомо выигрывают.
AG>Возможно польза лишь в универсальности — если отказаться от многообразия типов, то использовать только лишь знаковые легче, чем только лишь беззнаковые.
И это тоже (хотя неудобно при отображении на какие-то внешние сущности вроде машинных адресов).
Здравствуйте, T4r4sB, Вы писали:
TB>От нуля до N-1. На беззнаковых такой перебор записывается довольно неестественно. TB>Знак это дополнительный маркер, что мы вышли за диапазон в другую сторону.
Я буквально пару дней назад в похожую тему вцепился на хабре в треде про Rust, мне ответили:
=== cut here ===
Таким образом, для того, чтобы определить, какой код вызывается на каждой итерации цикла, нужно смотреть реализацию метода Range::next_back:
Как видно из кода, в структуре хранятся начало и конец диапазона, а так как диапазон не включает верхнюю границу, то верхняя граница уменьшается на единицу перед тем, как она возвращается, а критерием остановки является условие равенства (в общем случае — превосходства) нижней и верхней границы.
=== end cut ===
В чуть более сложном формате цикла, чем стандартный для C, такое сделать можно, но напрямую в стандартном — нет (надо как минимум в начале тела цикла делать выборку того, что тут описали как Some, из объекта итератора). Постдекремент в условии проверки — вариация на то же.
TB>Дааа, подумаешь, цикл через задницу написали, только из-за того, что какой-то идиот, обожравшийся тухлой рыбы, впихнул в СТЛ беззнаковые индексы.
Сколько экспрессии. Ты от возбуждения не заметил, что я даже не начинал стобой дискутировать ни на тему беззнаковых чисел, ни на тему STL. И не собираюсь даже я с тобой обсуждать эти темы. Я говорил только лишь об этом
Ты на беззнаках даже тупо от ЭН до нуля проитерироваться не можешь без дополнительного бубна.
Ты зря не послушал совета. Вот признал бы, что ступил, сейчас бы не было необходимости искать пресловутые "дополнительные бубны" там, где их нет, и доказывать, что белое — черное.
Здравствуйте, rg45, Вы писали:
R> Ты зря не послушал совета. Вот признал бы, что ступил
Перестань троллить, ты так и не смог привести пример с каноничным for, ты приводишь какие-то извраты и говоришь, что это норма.
Ты реально думал, что я не умею записывать обратный беззнаковый цикл хоть как-то?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, Poopy Joe, Вы писали:
H>>Во-вторых, иногда нужно не-валидное значение чего-то, что по смыслу неотрицательно. И для этого я лично использую «-1». И если переменная равна ему, то значит значение невалидное. Такая штука с unsigned не проканает, если не вводить всякие magic digits конечно. PJ>std::optional<uint>