Здравствуйте, cppguard, Вы писали:
C>А ведь как было бы приятно жить в мире доминирования неотрицательных чисел? Ошибки тогда просто уходили бы в INT_MAX, а используемый диапазон сокращался бы не вполовину, а всего на несколько элементов. Реально целые отрицательные используются для отрицательных смещений, разности двух величин и ... всё
Выбор int как типа по умолчанию не было ошибкой, все что семантически представляет собой целое число (неважно, может быть отрицательным или нет) должно быть по умолчанию знаковым типом. Реальная разница signed и unsigned не в отрицательности, их просто назвали неудачными именами. Из-за неудачного названия программисты теперь суют unsigned во все, что не может быть отрицательным.
Signed и unsigned следуют разной арифметике: signed представляет обычные числа которые следуют обычной арифметике в которой x-1 < x всегда true (именно из-за этого signed underflow/overflow навсегда объявлено UB), unsigned представляет машинные слова которые следуют modulo-2^N арифметике, где x-1 может быть как меньше так и больше x (well-defined)
В 99.99% случаев когда ты размышляешь об операциях с числами, ты и читатели твоего кода подразумевают обычную, а не модульную арифметику, так что и тип нужно использовать предназначенный для чисел, а не для машинных слов.
Поэтому C++ Core Guidelines, Google Style Guide и многие другие не рекомендуют использовать unsigned типы для чисел (только для флагов и битовых операций); Страуструп признал "беззнаковость" size_t исторической ошибкой; в новые стандарты введен ssize() возвращающий размеры в правильном типе.
Здравствуйте, netch80, Вы писали:
N>В любом случае, повторюсь, я поддерживаю, что в общем и целом со знаковыми удобнее хотя бы тем, что не надо следить за возможными проблемами перехода 0 -> 0-1. Вопрос был в конкретном примере.
Тема умения грамотно работать возле левого края интервалов вида [a, b) в условиях, когда выход за левый край интервала приводит к неожиданному, нежелательному или неопределенному поведению (ваши "проблемы перехода 0 -> 0-1"), является фундаментальным нативным свойством языков С и С++, да и вообще программирования в целом. Она распространяется не только на беззнаковые типы, но и на указательную арифметику, на итераторы, на потоки и на массу других концепций боле высокого уровня.
Поэтому попытки устранить эту "проблему" в области индексирования массивов — это локальное решение, которое ничего не поменяет в общем. Следить за "возможными проблемами перехода 0 -> 0-1" в более обобщенно понимании вам все равно придется ничуть не меньше чем ранее. Ценность "неудобной" беззнаковой арифметики в индексации заключается в том числе в том, что она с ранних пелёнок оповещает подрастающее поколение о существовании этой темы, с которой им в дальнейшем придется сталкиваться постоянно (независимо от того, какие типы они будут фактически использовать для индексации). Переход на знаковые типы лишь замедлит процесс формирования ценных идиом.
Никак не могу сопоставить в голове кое-что про коды ошибок. Со времён юникса тянется наследие в виде отрицательных значений при возврате кода ошибки целочисленным числом. Очевидно, что писать повсюду "err != -1" не очень-то приятно. Поэтому возникла мысль, что результат сохраняли в unsigned и вместо этого писали "err < INT_MAX", что читается легче и понятнее. НО! С тех же самых бородатых времён тянется наследие в виде уникальных платформ, про которые никто не слышал, правда, где underflow или overflow для int НЕ выполняется циклически, и таким образом приведение int к unsingned для отрицательных чисел считается UB (слышал, что хотят отменить).
А ведь как было бы приятно жить в мире доминирования неотрицательных чисел? Ошибки тогда просто уходили бы в INT_MAX, а используемый диапазон сокращался бы не вполовину, а всего на несколько элементов. Реально целые отрицательные используются для отрицательных смещений, разности двух величин и ... всё
Здравствуйте, reversecode, Вы писали:
R>компиляторы часто оптимизируют R>if (r == -1) R>преобразовывая в R>if (r < 0)
Приведите пример, пожалуйста, на каком-нибудь godbolt.
Конверсия между, например, x>0 и x!=0 (для unsigned), x<10 и x<=9 — да, такое сплошь и рядом.
А ваше — очень сомнительно.
Здравствуйте, B0FEE664, Вы писали:
АТ>>>>>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости. S>>>>И каков же в нашем мире результат вычитания 8 из 5? BFE>>>В реальном мире = 0. С оговоркой, что 0 — это пусто. N>>Точно не ноль. Скорее — исключение по невозможности совершить операцию. BFE> Исключение в реальном мире? Это как? BFE> Допустим вы полезли в холодильник с целью достать оттуда 8 куриных яиц, а там только 5. И тут случилось "исключение по невозможности совершить операцию". Что это может быть?
Обнаружили, что есть только 5 яиц. Задача не может быть выполнена. Действие достать яйца отменяется, происходит переход к более общей задаче и пересчёт ситуации. Может, надо изменить рецепт и ограничиться 5 яйцами. Может, надо сходить к соседям или в магазин. Много вариантов.
И, заметьте, обработка на более высоком уровне это один к одному, как если бы в коде стоял try-catch с ловлей ситуации "не хватило" в вызывающей функции.
Всё просто и банально, надо только рассмотреть реальность.
Вот если бы стояла задача "взять сколько есть яиц, но максимум 8" (причём с уточнением, что неважно, сколько реально взято! а то у вас и 5-8==0, и 3-8==0, и 0-8==0), то ваш вариант был бы адекватным задаче.
BFE>>>В мире идей = -3. BFE>>>Выберите свой мир. N>>Ваша вселенная удивительна, но людям там слегка не место. BFE>В современном мире реалисты встречаются редко: одни в бога верят, другие — в отрицательные числа...
Могу только порадоваться уровню вашей самокритики. Но адекватности ей это не даёт.
Здравствуйте, T4r4sB, Вы писали:
M>>Поэтому надо просто писать M>>
M>>for(auto i=0u; i!=v.size(); ++i)
M>>
TB>Это другой код. Для тех, кто не знает синтаксис Раста — интервалы вида a..b там полуоткрытые.
Я, получается, не знаю. То есть в вашем исходном примере for i in 0..v.len()-1 был перебор элементов не включая последний?
Ну тогда, извините, вы скорее всего ССЗБ, что не сделали проверку на непустоту массива, потому что такая операция на пустом массиве со всех точек зрения невозможна, и вместо неё должно выполняться что-то иное.
Здравствуйте, 5.März, Вы писали:
5M>Выбор int как типа по умолчанию не было ошибкой, все что семантически представляет собой целое число (неважно, может быть отрицательным или нет) должно быть по умолчанию знаковым типом. Реальная разница signed и unsigned не в отрицательности, их просто назвали неудачными именами. Из-за неудачного названия программисты теперь суют unsigned во все, что не может быть отрицательным.
5M>Signed и unsigned следуют разной арифметике: signed представляет обычные числа которые следуют обычной арифметике в которой x-1 < x всегда true (именно из-за этого signed underflow/overflow навсегда объявлено UB), unsigned представляет машинные слова которые следуют modulo-2^N арифметике, где x-1 может быть как меньше так и больше x (well-defined)
Нет, это вы приводите как раз позднюю _ре_интерпретацию подхода, которая возникла по сути из-за хакерского переиспользования недостаточно строгих правил предшествующих стандартов.
Первые компиляторы C выполняли операции строго 1:1 и ничего почти не оптимизировали. Всякие сокращения вычисления констант, конечно, уже были, но современного уровня серьёзных переделок не было. Операции выполнялись так, как сейчас получается в GCC/Clang с -fwrapv: переполнение игнорировалось. Но: из-за одновременного наличия машин как минимум с дополнительным кодом (>99%) и обратным (отдельные модели), стандарт не смог записать это (скорее, к сожалению) для знаковых операций. Причём, такого жёсткого разделения между undefined, unspecified и implementation-defined behavior, как сейчас, тогда не было: термины вводились, но достаточно наобум. И высокой опасности эффектов от UdB не было, операциями с ними пользовались напропалую.
А уже в 1990-х, когда начали вводить высокий уровень оптимизаций, фактически как злые хакеры, "переиспользовали" лазейку в стандарте с UdB для signed для оптимизаций. Но это было на ~20 лет позже.
Всё описанное известно по документам и воспоминаниям. Ссылок в явном виде я не собирал, но если попадутся и вспомню — закину сюда. Если горит, можно, например, попросить khim@habr (с ходу нашлось такое очень близкое, но он ещё высказывался), или John Regehr, у него в блоге есть современные последствия, но и предпосылки наверняка исследовал.
Для современного состояния, конечно, ваши слова актуальны. К сожалению. Я бы таки предпочёл, чтобы вначале зафиксировались на усечении для всех случаев: это бы ускорило введение нормальных средств, в которых реакция на нарушение области значения (обычно называемое переполнением) задаётся конкретным символом операции или контекстом (лучше всего в среднем). Это бы ускорило тут прогресс лет на 20-25.
Для сравнения: в Ada разделены диапазонные целочисленные типы (вместе с базовыми стандартными знаковыми и беззнаковыми) и модульные. У диапазонных по умолчанию проверяется результат при присвоении (и при многих арифметических операциях), генерируется исключение при выходе за диапазон; это заметно дороже, но надёжно. Для модульного типа (в стиле type byte = modulo 2**8) все операции по модулю, и там по типу видно, что будет усечение. Для идеального языка я ожидаю что-то среднее: по умолчанию проверка с исключениями, но при явном указании программистом — правила упрощаются вплоть до того, как сейчас в C со знаковыми — считается, что программист всё предусмотрел и переполнения (и прочих нарушений) не будет.
5M>В 99.99% случаев когда ты размышляешь об операциях с числами, ты и читатели твоего кода подразумевают обычную, а не модульную арифметику, так что и тип нужно использовать предназначенный для чисел, а не для машинных слов.
5M>Поэтому C++ Core Guidelines, Google Style Guide и многие другие не рекомендуют использовать unsigned типы для чисел (только для флагов и битовых операций);
Они именно так мотивируют? можно цитату? (с ходу не нашёл)
А то я бы предположил скорее сложность задания реакции на переход 0 -> -1 (как в примерах из этого
Because most arithmetic is assumed to be signed; x — y yields a negative number when y > x except in the rare cases where you really want modulo arithmetic.
По-моему, вам пора прекратить мучить сову.
5M> Страуструп признал "беззнаковость" size_t исторической ошибкой; в новые стандарты введен ssize() возвращающий размеры в правильном типе.
"Исторической ошибкой" size() является, конечно, в некоторой степени, но это факт не unsigned самого по себе — потому что размер не может быть отрицательным, а, значит, домен правильный. Страуструп как раз объясняет причины, а вы как-то очень странно читаете:
1. Там, где размер участвует в арифметике — почти всегда — естественно могут появляться отрицательные значения. Да, он это говорит.
Но: проблемные последствия этого являются следствием устоявшейся кривости стандарта, который не предусматривает иного действия для беззнаковых, кроме как по модулю. Если бы s-10, где s как size() какого-то контейнера равно 4, вызывало бы исключение — проблемы бы не было (или было бы сильно меньше). Считаете невозможным такой результат? — ok, не конвертировать к знаковым, пусть выбивает. Возможно, отрабатывать? — явно конвертировать.
2. Нужно явно конвертировать к знаковым для таких операций, из-за п.1. Но сейчас в C и C++ по правилам, если разная знаковость у операндов, они оба приводятся к беззнаковому.
Жёсткий надёжный язык тут бы скорее отказался компилировать — приведите к одной знаковости или ступайте себе лесом. Чуть более расслабленный пересчитал бы множество значений результата и проинтерпретировал по нему — и это тоже имеет смысл. Но автоконверсия C/C++ это таки точно наследие "бородатых годов".
3. (Страуструп не писал вроде, но я добавлю: ) Из-за (естественной) знаковости ptrdiff_t никакой контейнер байтов не может занимать более половины памяти (а с учётом кодирования в дополнительном коде — и ровно половина тоже недопустима).
Поэтому или разделять разные случаи (для байтовых одни правила, для прочих другие), или можно постановить, что size() любого контейнера не может превышать SSIZE_MAX.
Может, где-то есть платформа, где это даст неожиданные проблемы (например, 16-битный мир с одним массивом в адресах от 0x1000 до 0xEFFF), но их возможные проблемы проигнорировали, тем более что там и так непонятно, что делать — ptrdiff_t должен для корректной работы быть минимум 17-битным... на такой платформе и про STL сложно говорить всерьёз.
5M>C++ Core Guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-nonnegative 5M>Google Style Guide: https://google.github.io/styleguide/cppguide.html#Integer_Types 5M>B.Stroustrup “Subscripts and sizes should be signed”: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1428r0.pdf 5M>Длинное видео из 2013г где комитет C++ несколько раз признает ошибочность использования unsigned: https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything (12:10, 42:40, 1:02:50)
Последняя ссылка битая, видео, наверно, удалено.
По поводу остальных ссылок, удивительно, как вы при наличии прямых указаний авторов умудряетесь переинтерпретировать вплоть до полной противоположности.
Здравствуйте, 5.März, Вы писали:
5M>Для меня классикой уже стало наблюдать, как русские программисты выходят из тайги, отрицают все источники и авторитеты, и делают по-своему просто чтобы почувствовать себя чуть умнее, чем на самом деле. Откуда у нас столько одаренных людей берется, которые лучше знают что нужно дизайну языка, чем дизайнеры языка, ума не приложу
Страуструп создатель, а не дизайнер, дизайнер это коммитет. Точно так же, как Java сейчас разрабатывает Оракл, а не Гослинг. Авторитеты в инженерном деле это идолопоклонничество. А людям из тайги своё мнение не положено, да?
5M>Он не учит вас жизни, а приводит аргументы, что знаковые были бы для языка лучше. Есть контраргументы — высказывайте... Или в универе больше не учат, что обсуждение личности человека не является опровержением его аргументов? Почему я в англоязычных дискуссиях подобного невежества не наблюдаю?
Потому что все нетоксичные, предельно вежливые и прочее, так заведено. Я видел обсуждения внутренних групп FAANG касательно всяких священных войн, и С++ в то числе, так там яд разве что через монитор не брызгал, но внешне, особенно для ex-USSR населения, всё выглядело чинно-благородно. А самое непрятное в таких обсуждениях то, что очень быстро от фактов и логических умозаключений переходят к "the results of most computations are signed" (слова Страуструпа), то есть полностью субъективному мнению, а мнение это имеет очень большой вес. Вот и приходят в выводу "почему-бы не прислушаться к словам мэтра, он же велик". А код при этом сопровождают простые инженеры.
5M>В дотнете и тысяче других более-менее новых языков тоже все размеры и индексы знаковые, угадайте почему, вы бы лучше учились чем умничать.
Я где-то писал, что индексы должны быть беззнаковые?
Здравствуйте, cppguard, Вы писали:
C>А ведь как было бы приятно жить в мире доминирования неотрицательных чисел? Ошибки тогда просто уходили бы в INT_MAX, а используемый диапазон сокращался бы не вполовину, а всего на несколько элементов. Реально целые отрицательные используются для отрицательных смещений, разности двух величин и ... всё
Здравствуйте, cppguard, Вы писали:
C>Никак не могу сопоставить в голове кое-что про коды ошибок. Со времён юникса тянется наследие в виде отрицательных значений при возврате кода ошибки целочисленным числом. Очевидно, что писать повсюду "err != -1" не очень-то приятно. Поэтому возникла мысль, что результат сохраняли в unsigned и вместо этого писали "err < INT_MAX", что читается легче и понятнее. НО! С тех же самых бородатых времён тянется наследие в виде уникальных платформ, про которые никто не слышал, правда, где underflow или overflow для int НЕ выполняется циклически, и таким образом приведение int к unsingned для отрицательных чисел считается UB (слышал, что хотят отменить).
C>А ведь как было бы приятно жить в мире доминирования неотрицательных чисел? Ошибки тогда просто уходили бы в INT_MAX, а используемый диапазон сокращался бы не вполовину, а всего на несколько элементов. Реально целые отрицательные используются для отрицательных смещений, разности двух величин и ... всё
Всё очень просто 0-нет ошибки, не 0 — есть ошибка и всё.
Подведу итог:
1) в стартовом посте написан сумбур
2) никто ничего не сохранял в unsigned
3) всегда сравнение было с нулём, причем 0 — это всё ок, -1 — это невалидное значение, 1+ — это коды ошибок
4) уже давно вместо этого используют typed enum — чего и вам советую
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Во-первых, поведение целочисленных типов при приведении от signed к unsigned четко определено стандартом языка. Никаких неожиданностей здесь нет и никакого "наследия уникальных платформ" нет тоже. Если вам нравится сохранять результат в `unsigned` и проверять на `err < INT_MAX` — флаг вам в руки. Возможно тут есть какие-то подводные камни, но в общем и целом это будет гарантированно работать. Почему вы считаете, что это "лучше" проверки на отрицательность, я в упор не вижу, но если вам так больше нравится — вперед.
Потому что такой код проще формально доказывать. А конструкция "err ! =-1" подразумевает, что err может быть -2, например, хотя описание функции чётко говорит "положительное или -1". Это один момент. Второй момент — когда читаешь такой код, то сразу начинаешь думать "так, у нас может быть -1, а -2 может быть? А что это вообще за функция? Это POSIX? Возвращает какой-то handle типа файла или сокета или PID или что-то другое?". Кода возврата программ, кстати, почему-то неотрицательные.
АТ>Во-вторых, возвращаясь к вопросу знакового переполнения ("для int"), о каких "уникальных платформах" вы ведете речь? Вы о чем вообще? Постарайтесь понять простую вещь: основным источником неопределенного поведения в С и С++ является не аппаратная платформа, а компилятор. Компилятор, компилятор и только компилятор. То есть те предположения о "невозможности" неопределенного поведения, которые он широко, охотно и активно использует в процессе оптимизации кода. Неопределенное поведение в С и С++ существует именно для этого: чтобы помочь компилятору в оптимизации кода. На всех С и С++ платформах, без единого исключения, знаковые типы НЕ заворачиваются при переполнении. Никакой "уникальности" или "бородатости" в этом нет. Что за чушь? Ни на одной современной С или С++ платформе `int` НЕ заворачивается. С/С++ платформ с заворачивающимся `int` не существует и не существовало нигде и никогда, кроме, возможно, вашего воображения. Ни "уникальных", ни "повсеместных", ни "бородатых", ни "бритых" — никаких.
АТ>В-третьих, почему вы вообще ведете речь о "underflow или overflow для int"? Приведения типов (между signed и unsigned) никогда не вызывали "переполнений" в С и С++. Как я сказал выше, приведение от signed к unsigned определено стандартом. Приведение от unsigned к signed всегда было implementation defined, но сейчас стандарты С и С++ четко определяют и его тоже. При целочисленном приведении нет никакого "underflow или overflow для int" даже отдаленно. "Overflow для int" существует только в процессе выполнения арифметических операций над int, но не в процессе выполнения приведений. Так что к чему вы вообще приплели тут "underflow или overflow для int" — в упор не ясно.
Да, тут я дал маху и перепутал приведение типов и переполнение при арифметических операциях. В ОП, конечно же, речь шла о приведении типов.
Здравствуйте, netch80, Вы писали:
N>1. Там, где размер участвует в арифметике — почти всегда — естественно могут появляться отрицательные значения. Да, он это говорит.
N>Но: проблемные последствия этого являются следствием устоявшейся кривости стандарта, который не предусматривает иного действия для беззнаковых, кроме как по модулю. Если бы s-10, где s как size() какого-то контейнера равно 4, вызывало бы исключение — проблемы бы не было (или было бы сильно меньше). Считаете невозможным такой результат? — ok, не конвертировать к знаковым, пусть выбивает. Возможно, отрабатывать? — явно конвертировать.
Я пишу на Расте и проблема никуда не делась.
Обычный заголовок
for i in 0 .. v.len()-1
Является валидным полностью рабочим кодом для знакового индекса. Но в Расте этот заголовок падает на пустом векторе. В С++ я бы получил ассерт при обращении по инвалидному индексу.
Внимание вопрос: а какая мне нахрен разница, что программа упала не с дурацким старомодным отсталым ассертом, а с красивой модной прогрессивной молодёжной паникой?
Так что нет, индексы и размеры должны быть знаковыми и только знаковыми.
Ваши действия? Только я попрошу рассмотреть все возможные случаи, а не тот, где вы наизусть знаете кодовую базу. И если ответ будет "пойду смотреть, что там может вернуть my_read()", то это, конечно, корректный ответ, но далеко так не уехать, потому что my_read легко может стать my_fucking_long_func размером строчек 500. Или вообще не иметь исходного кода и, например, линковаться на последнем этапе или подтягиваться из сгенерированных исходников.
P.S. Да, я последнее время как-то увлёкся темой mission critical, формальными доказательствами и прочей, неинтересной в нормальном мире ерундой. Но это не делает мои аргументы бессмысленными. Почитайте, хотя бы, блог PVS-Studio и ужаснитесь количеству глупых ошибок в крупных открытых проектах, которые, очевидно, попали в мастер по недоглядке.
Здравствуйте, Don Reba, Вы писали:
M>>>Поэтому надо просто писать M>>>
M>>>for(auto i=0u; i!=v.size(); ++i)
M>>>
N>>А теперь повторите это для прохождения по элементам в порядке убывания индексов.
DR>
DR>for (auto i = v.size(); i --> 0;)
DR>
Да, знаменитый приём (и я его рядом упоминал). Но уже с хаком в виде постинкремента, и конструкция цикла тут совсем другая, и безусловный переход из 0 в (size_t)-1 это некрасиво и в общем случае непереносимо. Автоматически до него ~99% пишущих не додумаются, это надо видеть. Вместо него, скорее всего, будет сделан цикл с постпроверкой.
Итого — совсем не эквивалент.
Здравствуйте, sergii.p, Вы писали:
SP>уж что-что, но это не обычный заголовок. В данном случае .. в терминах математики означает [0; v.len()-1). Последний элемент не включается. То есть вы ещё один элемент откусили от вектора.
Всё верно, мне надо перебрать все элементы кроме последнего. И почему в этом "безопасном" языке даже такая простая конструкция скрывает в себе проблемы?
SP>Вообще циклы добавили в раст для выявления профнепригодности. Итераторы же есть.
Ах, ну да, я же забыл, пенсионерские отсталые циклы уже не в моде в этом сезоне, сейчас модная прогрессивная молодёжь использует клёвые весёлые итераторы.
Вот только индексация универсальнее итератора, и оптимизируется лучше.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, Sinclair, Вы писали:
АТ>>>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости. S>>И каков же в нашем мире результат вычитания 8 из 5? BFE>В реальном мире = 0. С оговоркой, что 0 — это пусто.
Точно не ноль. Скорее — исключение по невозможности совершить операцию.
BFE>В мире идей = -3. BFE>Выберите свой мир.
Ваша вселенная удивительна, но людям там слегка не место.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, Sinclair, Вы писали:
АТ>>>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости. S>>И каков же в нашем мире результат вычитания 8 из 5? BFE>В реальном мире = 0. С оговоркой, что 0 — это пусто.
Ну, то есть в вашем реальном мире 5-8 == 7-8, я правильно понимаю? А 10 + (5-7) и 10 + (7-8) тоже будут равны между собой?
Занятная алгебра у вас там, в "реальном мире". BFE>В мире идей = -3. BFE>Выберите свой мир.
.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
АТ>>>>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости. S>>>И каков же в нашем мире результат вычитания 8 из 5? BFE>>В реальном мире = 0. С оговоркой, что 0 — это пусто. S>Ну, то есть в вашем реальном мире 5-8 == 7-8, я правильно понимаю? А 10 + (5-7) и 10 + (7-8) тоже будут равны между собой? S>Занятная алгебра у вас там, в "реальном мире".
Ээээ, вообще-то [0, -1), и [0, -2) тоже — это пустой диапазон
Отрицание существования отрицательных чисел интуитивно согласуется с этим утверждением.
Ну, а если вы думаете, что отрицательные числа существуют, то покажите мне минус пять яблок. Именно яблок, а не их огрызков.
BFE>>В мире идей = -3. BFE>>Выберите свой мир. S>.
На дворе коронавирус, а вы себя за лицо хватаете...
Числа — это всё условности:
for(unsigned i = 2; i != -4; --i)
std::cout << i << std::endl;
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости. S>И каков же в нашем мире результат вычитания 8 из 5?
В мире количественных вычислений не бывает не только "результата вычитания 8 из 5", но не бывает даже и необходимости вычитать 8 из 5. Возникновение такой необходимости в каких-либо выкладках — это звоночек, говорящий, что где-то раньше вы совершили ошибку и забрели не в ту степь.
Это примерно как спрашивать, чему равна сумма трех авторучек и пяти литров чернил.
Здравствуйте, cppguard, Вы писали:
C>Никак не могу сопоставить в голове кое-что про коды ошибок. Со времён юникса тянется наследие в виде отрицательных значений при возврате кода ошибки целочисленным числом. Очевидно, что писать повсюду "err != -1" не очень-то приятно.
Зато писать "err<0" вполне приятно.
C>Поэтому возникла мысль, что результат сохраняли в unsigned и вместо этого писали "err < INT_MAX", что читается легче и понятнее. НО! С тех же самых бородатых времён тянется наследие в виде уникальных платформ, про которые никто не слышал, правда, где underflow или overflow для int НЕ выполняется циклически,
Причем тут уникальность каких платформ? Такое на любой платформе можно получить довольно легко
C>и таким образом приведение int к unsingned для отрицательных чисел считается UB (слышал, что хотят отменить).
Где ты такое вычитал?
C>А ведь как было бы приятно жить в мире доминирования неотрицательных чисел? Ошибки тогда просто уходили бы в INT_MAX, а используемый диапазон сокращался бы не вполовину, а всего на несколько элементов. Реально целые отрицательные используются для отрицательных смещений, разности двух величин и ... всё
Очень неудачное решение.
Можно возвращать не просто -1, а отрицательный код ошибки, вместо сохранения его в errno, например.
У меня есть самописные контейнеры, например, кольцевой буфер, там тип размера — параметр шаблона, и я могу использовать uint8_t/uint16_t для экономии памяти, а для инвалидного индекса есть npos.
Здравствуйте, cppguard, Вы писали:
C>Никак не могу сопоставить в голове кое-что про коды ошибок. Со времён юникса тянется наследие в виде отрицательных значений при возврате кода ошибки целочисленным числом. Очевидно, что писать повсюду "err != -1" не очень-то приятно. Поэтому возникла мысль, что результат сохраняли в unsigned и вместо этого писали "err < INT_MAX", что читается легче и понятнее. НО! С тех же самых бородатых времён тянется наследие в виде уникальных платформ, про которые никто не слышал, правда, где underflow или overflow для int НЕ выполняется циклически, и таким образом приведение int к unsingned для отрицательных чисел считается UB (слышал, что хотят отменить).
Какая-то непонятная чепуха написана.
Во-первых, поведение целочисленных типов при приведении от signed к unsigned четко определено стандартом языка. Никаких неожиданностей здесь нет и никакого "наследия уникальных платформ" нет тоже. Если вам нравится сохранять результат в `unsigned` и проверять на `err < INT_MAX` — флаг вам в руки. Возможно тут есть какие-то подводные камни, но в общем и целом это будет гарантированно работать. Почему вы считаете, что это "лучше" проверки на отрицательность, я в упор не вижу, но если вам так больше нравится — вперед.
Во-вторых, возвращаясь к вопросу знакового переполнения ("для int"), о каких "уникальных платформах" вы ведете речь? Вы о чем вообще? Постарайтесь понять простую вещь: основным источником неопределенного поведения в С и С++ является не аппаратная платформа, а компилятор. Компилятор, компилятор и только компилятор. То есть те предположения о "невозможности" неопределенного поведения, которые он широко, охотно и активно использует в процессе оптимизации кода. Неопределенное поведение в С и С++ существует именно для этого: чтобы помочь компилятору в оптимизации кода. На всех С и С++ платформах, без единого исключения, знаковые типы НЕ заворачиваются при переполнении. Никакой "уникальности" или "бородатости" в этом нет. Что за чушь? Ни на одной современной С или С++ платформе `int` НЕ заворачивается. С/С++ платформ с заворачивающимся `int` не существует и не существовало нигде и никогда, кроме, возможно, вашего воображения. Ни "уникальных", ни "повсеместных", ни "бородатых", ни "бритых" — никаких.
В-третьих, почему вы вообще ведете речь о "underflow или overflow для int"? Приведения типов (между signed и unsigned) никогда не вызывали "переполнений" в С и С++. Как я сказал выше, приведение от signed к unsigned определено стандартом. Приведение от unsigned к signed всегда было implementation defined, но сейчас стандарты С и С++ четко определяют и его тоже. При целочисленном приведении нет никакого "underflow или overflow для int" даже отдаленно. "Overflow для int" существует только в процессе выполнения арифметических операций над int, но не в процессе выполнения приведений. Так что к чему вы вообще приплели тут "underflow или overflow для int" — в упор не ясно.
Здравствуйте, cppguard, Вы писали:
C>Никак не могу сопоставить в голове кое-что про коды ошибок. Со времён юникса тянется наследие в виде отрицательных значений при возврате кода ошибки целочисленным числом. Очевидно, что писать повсюду "err != -1" не очень-то приятно. Поэтому возникла мысль, что результат сохраняли в unsigned и вместо этого писали "err < INT_MAX", что читается легче и понятнее.
Нет, не писали. Нет, не понятнее.
И это только в ядерном API возврат -1 для всех ошибок. Есть такие, в которых -1, -2, ...
Поэтому "< 0" универсальнее.
C> НО! С тех же самых бородатых времён тянется наследие в виде уникальных платформ, про которые никто не слышал, правда, где underflow или overflow для int НЕ выполняется циклически, и таким образом приведение int к unsingned для отрицательных чисел считается UB (слышал, что хотят отменить).
C++20 — уже отменили. C — на очереди.
Разница между ==1 и <0 при единственном -1 тут не появляется. А вот подозрительные конверсии — не в тему.
C>А ведь как было бы приятно жить в мире доминирования неотрицательных чисел? Ошибки тогда просто уходили бы в INT_MAX, а используемый диапазон сокращался бы не вполовину, а всего на несколько элементов. Реально целые отрицательные используются для отрицательных смещений, разности двух величин и ... всё
И это будет решение частного случая, а не общего подхода, причём предельно кривым методом.
Вы не хотите взглянуть чуть-чуть шире. На самом деле у этих функций не какие-то -1 или прочее, это уже результат укладки в доступные (для дорогого хардвера 1970-х) возможности. Все эти функции, по сути, возвращают то, что в C++ называется variant, в Rust — enum:
enum errno { коды; };
struct normal_rvalue { int v; };
using syscall_retval = std::variant<normal_rvalue, errno>;
Это и отражается соответственно в ядерном ABI. Например, FreeBSD на x86 использует такое: если при выходе из сисколла CF=0, то в EAX/RAX нормальное значение, оно и передаётся. Если CF=1, то RAX пишется в errno текущей нити и заменяется на -1.
Ряд более новых функций, типа семейства pthread_*, возвращают 0 нормально и код errno в случае ошибки, там такого костыля, как выше, нет.
А вот если бы такой variant был в явном виде с самого начала — вы бы это и проверяли по типу (псевдокод):
match open("/dev/null", O_RDONLY) {
case normal_rvalue(int newfd): ...
case error(int err): ...
c> А ведь как было бы приятно жить в мире доминирования неотрицательных чисел?
Здравствуйте, netch80, Вы писали:
N>C++20 — уже отменили. C — на очереди.
UB при signed overflow никто в С++20 не отменял. То, что signed overflow на самом деле не имеет никакого отношения к теме, поднятой автора вопроса — дело другое, но тем не менее....
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Здравствуйте, netch80, Вы писали:
N>>C++20 — уже отменили. C — на очереди.
АТ>UB при signed overflow никто в С++20 не отменял. То, что signed overflow на самом деле не имеет никакого отношения к теме, поднятой автора вопроса — дело другое, но тем не менее....
Спасибо за уточнение. Я "отменили" написал к словам комментатора
C>> НО! С тех же самых бородатых времён тянется наследие в виде уникальных платформ, про которые никто не слышал, правда, где underflow или overflow для int НЕ выполняется циклически
Здравствуйте, Андрей Тарасевич, Вы писали:
AT>На всех С и С++ платформах, без единого исключения, знаковые типы НЕ заворачиваются при переполнении. Никакой "уникальности" или "бородатости" в этом нет. Что за чушь? Ни на одной современной С или С++ платформе `int` НЕ заворачивается. С/С++ платформ с заворачивающимся `int` не существует и не существовало нигде и никогда, кроме, возможно, вашего воображения. Ни "уникальных", ни "повсеместных", ни "бородатых", ни "бритых" — никаких.
`gcc -fwrapv`, `clang -fwrapv` это столь же законные и легкодоступные платформы, как и просто `gcc` или `clang`, почти повсеместные, и они именно "с заворачивающимся `int`". Зачем вы говорите про их несуществование, да ещё и с таким апломбом, мне непонятно.
И оно реально используется: CPython2 компилируется именно с -fwrapv (при том, что с -O3). (В 3-м это устранили — видно, вычистили проблемные места.)
Разумеется, имеет смысл дописать, что использовать такое плохо (некошерно, некузяво и т.п.), но это не вопрос формальной возможности.
AT> Приведение от unsigned к signed всегда было implementation defined, но сейчас стандарты С и С++ четко определяют и его тоже.
И где такое сказано для C?
C17 (N2176) и последний на сейчас драфт (N2731), 6.3.1.3(3):
Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
AT>При целочисленном приведении нет никакого "underflow или overflow для int" даже отдаленно.
Как же нет, если аргумент не вмещается в область значений результата.
Может, вам не нравится слово "переполнение" для этого, но это синоним де факто.
То, что большинство компиляторов реализуют его как bit cast, это таки I-D. Сами ж хотели все платформы, а не только основные.
АТ>Какая-то непонятная чепуха написана.
Здравствуйте, 5.März, Вы писали:
5M>Signed и unsigned следуют разной арифметике: signed представляет обычные числа которые следуют обычной арифметике в которой x-1 < x всегда true (именно из-за этого signed underflow/overflow навсегда объявлено UB)
И это большая проблема С/С++ — нет стандартного способа проверить, что у тебя всё ок с арифметикой, потому что компилятор тупо вырезает проверки вида assert(a+b>a). Если бы в стандарте был описан отладочный режим, при котором переполнение знаковых это остановка программы и вызов отладчика / завершение программы, то было бы всё не так плохо, но такой режим есть только в виде личной инициативы отдельных компиляторов.
Здравствуйте, AeroSun, Вы писали:
AS>Подведу итог:
Много берёте на себя, и в принципиальных моментах неверно.
AS>3) всегда сравнение было с нулём, причем 0 — это всё ок, -1 — это невалидное значение, 1+ — это коды ошибок
НЕТ.
Если говорить в контексте типового Unix API, то есть минимум 4 разных случая:
1. Нет ошибки — 0, ошибка — -1 и в errno сохранён код ошибки (>0).
Выполняется для функций, у которых только признак успех/неудача (как close(), ioctl(), setuid()...), или результат идёт другим каналом (указатель в аргументах, например: pipe() — массив на 2 дескриптора).
2. Нет ошибки — >=0, ошибка — -1 и в errno сохранён код ошибки (>0). Это open(), read(), write(), fcntl() и десятки других.
3. Нет ошибки — 0, ошибка — код ошибки больше 0, обычно из errno. Результат всегда идёт другим каналом (по указателю в аргументах). Это вся группа pthread_*, это getaddrinfo(), getnameinfo() и некоторые другие.
(Кстати, если ошибки нет, варианты 1-3 могут менять errno, а могут и не менять.)
4. Индикации ошибки в возвращаемом значении нет вообще (или слишком много вариантов). Для проверки на ошибку надо смотреть на errno (предварительно обнулив его) и на другие признаки.
Пример: strtol(): если значение вышло за пределы — она пишет ERANGE в errno. Если после числа в строке мусор — надо смотреть на *endptr != 0. Если на входе пустая строка — ответ 0, errno == 0, но факт пустой строки определяется по endptr == beginptr.
А вот варианта, что вы описали, именно в этой комбинации, просто не существует.
AS>1) в стартовом посте написан сумбур
Топикстартер хотел разобраться. Как известно, чтобы получить ответ, надо в вопросе сформулировать его половину. Сумбур — не страшно, главное — ключевые слова.
AS>2) никто ничего не сохранял в unsigned
Тут единственное что верно, с поправкой на то, что на уровне ассемблера могли вообще не смотреть на знак.
AS>4) уже давно вместо этого используют typed enum — чего и вам советую
Перевести на него существующее API пока как-то невозможно.
AS>Подведу итог: AS>1) в стартовом посте написан сумбур
Лишь толко часть, где я спутал переполнение и приведение.
AS>2) никто ничего не сохранял в unsigned
Да, об этом-то и речь. Почему?
AS>3) всегда сравнение было с нулём, причем 0 — это всё ок, -1 — это невалидное значение, 1+ — это коды ошибок
Если код, например, для автопилота, то нужно будет обработать -2, даже если оно там не планируется. Или доказать, что -2 не появится ни при каких условиях, что сделать труднее. Symbolic execution становится сложнее.
AS>4) уже давно вместо этого используют typed enum — чего и вам советую
Я вообще не пишу на С++
TB>Является валидным полностью рабочим кодом для знакового индекса. Но в Расте этот заголовок падает на пустом векторе. В С++ я бы получил ассерт при обращении по инвалидному индексу.
TB>Внимание вопрос: а какая мне нахрен разница, что программа упала не с дурацким старомодным отсталым ассертом, а с красивой модной прогрессивной молодёжной паникой?
TB>Так что нет, индексы и размеры должны быть знаковыми и только знаковыми.
Здравствуйте, T4r4sB, Вы писали:
N>>1. Там, где размер участвует в арифметике — почти всегда — естественно могут появляться отрицательные значения. Да, он это говорит.
N>>Но: проблемные последствия этого являются следствием устоявшейся кривости стандарта, который не предусматривает иного действия для беззнаковых, кроме как по модулю. Если бы s-10, где s как size() какого-то контейнера равно 4, вызывало бы исключение — проблемы бы не было (или было бы сильно меньше). Считаете невозможным такой результат? — ok, не конвертировать к знаковым, пусть выбивает. Возможно, отрабатывать? — явно конвертировать.
TB>Я пишу на Расте и проблема никуда не делась. TB>Обычный заголовок
TB>
TB>for i in 0 .. v.len()-1
TB>
TB>Является валидным полностью рабочим кодом для знакового индекса. Но в Расте этот заголовок падает на пустом векторе.
[UPDATED:] На C++ это переводится: for (unsigned i = 0; i < v.len()-1; ++i)
То есть, последний элемент не используется.
Вначале я прочёл это как закрытый интервал, поспешил.
Ну тогда это вообще некорректно, потому что подобная операция на пустом контейнере не должна рассматриваться, а обдумывание должно уйти на уровень выше (чего вообще хотели достичь).
Если же речь шла бы о полуоткрытом справа интервале по всем элементам:
for i in 0 ..< v.len() (".." и есть моё "..<" для Rust)
то и проверять его надо было бы через '<', а не '<='.
дискуссию, где топикстартер тянет одеяло в противоположную сторону там и Rust вспоминали, и проблемы, как при таком итерировании и где использовать цикл с предпроверкой, а где — с постпроверкой.
Вы там участвовали, и я как раз отвечал ссылкой на комментарий на Хабре. Но я оставлю ссылку для других.
TB> В С++ я бы получил ассерт при обращении по инвалидному индексу.
Кстати, если доступ к элементу по [], а не at(), то и ассерта бы не было — скорее всего был бы сегфолт.
TB>Внимание вопрос: а какая мне нахрен разница, что программа упала не с дурацким старомодным отсталым ассертом, а с красивой модной прогрессивной молодёжной паникой? TB>Так что нет, индексы и размеры должны быть знаковыми и только знаковыми.
Да, это общий вывод оппонентов EM в той дискуссии.
Я скорее поддерживаю, потому что польза от корректности перехода 0 <-> -1 налицо, а коллекция больше SSIZE_MAX маловероятна (рядом писал).
в целом это всё равно проблема кривости исходных средств (всех: и стандартных манер арифметики в C, и то, что у вас там в Rust оно не хочет из-за этого создавать просто пустое множество...)
Здравствуйте, cppguard, Вы писали:
N>>Отрицательные пиксельные координаты — повсюду.
C>Экранные? Это где, например? Графику мы не берём, там сразу floating-point.
Не экранные, а на картинке. Сдвигаешь что-нибудь и оно выходит за пределы кадра или карты. Объект детектируется нейросеткой у края, особенно если часть его уже вышла за пределы — сразу или в минус, или в плюс за кадр.
Картинку повернул н какой-то угол — края уже не поместились.
5M>>Signed и unsigned следуют разной арифметике: signed представляет обычные числа которые следуют обычной арифметике в которой x-1 < x всегда true (именно из-за этого signed underflow/overflow навсегда объявлено UB), unsigned представляет машинные слова которые следуют modulo-2^N арифметике, где x-1 может быть как меньше так и больше x (well-defined)
N>Нет, это вы приводите как раз позднюю _ре_интерпретацию подхода, которая возникла по сути из-за хакерского переиспользования недостаточно строгих правил предшествующих стандартов.
Поздняя — это когда? Предшествующие стандарты — это какие?
Здравствуйте, σ, Вы писали:
5M>>>Signed и unsigned следуют разной арифметике: signed представляет обычные числа которые следуют обычной арифметике в которой x-1 < x всегда true (именно из-за этого signed underflow/overflow навсегда объявлено UB), unsigned представляет машинные слова которые следуют modulo-2^N арифметике, где x-1 может быть как меньше так и больше x (well-defined)
N>>Нет, это вы приводите как раз позднюю _ре_интерпретацию подхода, которая возникла по сути из-за хакерского переиспользования недостаточно строгих правил предшествующих стандартов.
σ>Поздняя — это когда? Предшествующие стандарты — это какие?
1990-е и позже. Примерно тогда, когда начали использовать оптимизации, описанные Мучником (могут быть и другие источники, но его книга самая показательная).
Предшествующие это ANSI 1985-го года (первый который был вообще стандартом за пределами Bell Labs) и ISO 1989-го.
5M>>>>Signed и unsigned следуют разной арифметике: signed представляет обычные числа которые следуют обычной арифметике в которой x-1 < x всегда true (именно из-за этого signed underflow/overflow навсегда объявлено UB), unsigned представляет машинные слова которые следуют modulo-2^N арифметике, где x-1 может быть как меньше так и больше x (well-defined)
N>>>Нет, это вы приводите как раз позднюю _ре_интерпретацию подхода, которая возникла по сути из-за хакерского переиспользования недостаточно строгих правил предшествующих стандартов.
σ>>Поздняя — это когда? Предшествующие стандарты — это какие?
N>1990-е и позже. Примерно тогда, когда начали использовать оптимизации, описанные Мучником (могут быть и другие источники, но его книга самая показательная).
N>Предшествующие это ANSI 1985-го года (первый который был вообще стандартом за пределами Bell Labs) и ISO 1989-го.
В ANSI C89 Rationale написано, что unsigned это неудачное название для модульной арифметики. Не очень похоже на "более позднюю реинтерпретацию"
Здравствуйте, netch80, Вы писали:
N>Ну тогда, извините, вы скорее всего ССЗБ, что не сделали проверку на непустоту массива,
А зачем её делать-тоааа?! На знаковых индексах я получал бы 100% валидный код, который на пустом массиве бы ничего не делал, что тут плохого? Почему этот беззнаковый дебилизм вынуждает меня усложнять код?!
Здравствуйте, σ, Вы писали:
σ>>>Поздняя — это когда? Предшествующие стандарты — это какие?
N>>1990-е и позже. Примерно тогда, когда начали использовать оптимизации, описанные Мучником (могут быть и другие источники, но его книга самая показательная).
N>>Предшествующие это ANSI 1985-го года (первый который был вообще стандартом за пределами Bell Labs) и ISO 1989-го.
σ>В ANSI C89 Rationale написано, что unsigned это неудачное название для модульной арифметики. Не очень похоже на "более позднюю реинтерпретацию"
Скачал один похожий документ (от 1988). Вижу пассаж:
The keyword unsigned is something of a misnomer, suggesting as it does arith-
metic that is non-negative but capable of overflow. The semantics of the C type
unsigned is that of modulus, or wrap-around, arithmetic, for which overflow has
no meaning. The result of an unsigned arithmetic operation is thus always defined,
whereas the result of a signed operation may (in principle) be undefined. In prac-
tice, on twos-complement machines, both types often give the same result for all
operators except division, modulus, right shift, and comparisons. Hence there has
been a lack of sensitivity in the C community to the differences between signed and
unsigned arithmetic (see §3.2.1.1).
Вы про него?
OK, тогда хронологию надо чуть сдвинуть. К 85-му это ещё толком не поняли. Это Rationale, как видно, от 88-го. Разница в 4 года по сравнению со стартом C (1970-е) несущественна. Спасибо, документ сохраню — как-то я его пропустил.
Но вот последнее предложение цитаты показывает, что окончательного решения по поводу, что делать со знаковой арифметикой, не было. Это основное, на что я тут намекал. Именно в промежутке между C89 и C99 сформировалась идея абьюза предыдущей зависимости от представления отрицательных — совсем не под то, из чего она возникла.
Там ещё интересно про споры про вариант integral promotion (расширять до signed или unsigned?) на страницах 34-35.
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, netch80, Вы писали:
N>>Ну тогда, извините, вы скорее всего ССЗБ, что не сделали проверку на непустоту массива,
TB>А зачем её делать-тоааа?! На знаковых индексах я получал бы 100% валидный код, который на пустом массиве бы ничего не делал, что тут плохого? Почему этот беззнаковый дебилизм вынуждает меня усложнять код?!
Я полностью согласен, что был бы "100% валидный код, который на пустом массиве бы ничего не делал".
Я не согласен, что в этом случае вообще в этот код надо как-то входить. Задача, где пропускается последний элемент, при пустом массиве означает (практически наверняка) вообще некорректную постановку, если этот массив законный, или нарушение в данных. Мой комментарий был исключительно об этом.
И поэтому плохо приводить её как пример.
Пример с перебором назад, который я упоминал, и который обсуждался в прошлой дискуссии, тут лучше. Если у вас N элементов с нумерацией от 0 до N-1, то цикл вида
for (unsigned i = N-1; i >= 0; --i) {
...
}
некорректен — нужно или менять на пост-проверку, или постинкремент (знаменитый хак), или на знаковые. Сама же задача так пройтись по элементам не имеет никакой проблемы в постановке.
Здравствуйте, netch80, Вы писали:
N>Я не согласен, что в этом случае вообще в этот код надо как-то входить. Задача, где пропускается последний элемент, при пустом массиве означает (практически наверняка) вообще некорректную постановку, если этот массив законный, или нарушение в данных. Мой комментарий был исключительно об этом.
Ну, это не так. Взять тот же случай, когда надо перебрать пары элементов.
Ваши действия? Только я попрошу рассмотреть все возможные случаи, а не тот, где вы наизусть знаете кодовую базу. И если ответ будет "пойду смотреть, что там может вернуть my_read()", то это, конечно, корректный ответ, но далеко так не уехать, потому что my_read легко может стать my_fucking_long_func размером строчек 500. Или вообще не иметь исходного кода и, например, линковаться на последнем этапе или подтягиваться из сгенерированных исходников.
P.S. Да, я последнее время как-то увлёкся темой mission critical, формальными доказательствами и прочей, неинтересной в нормальном мире ерундой. Но это не делает мои аргументы бессмысленными. Почитайте, хотя бы, блог PVS-Studio и ужаснитесь количеству глупых ошибок в крупных открытых проектах, которые, очевидно, попали в мастер по недоглядке.
Это один из хороших примеров "Страуструпа несущего чушь". К сожалению, под видом натягивания этой знаковой совы на глобус `std::span` он одновременно пытается натянуть эту сову на весь язык.
Здравствуйте, cppguard, Вы писали:
C>Ваши действия? Только я попрошу рассмотреть все возможные случаи, а не тот, где вы наизусть знаете кодовую базу. И если ответ будет "пойду смотреть, что там может вернуть my_read()", то это, конечно, корректный ответ, но далеко так не уехать, потому что my_read легко может стать my_fucking_long_func размером строчек 500. Или вообще не иметь исходного кода и, например, линковаться на последнем этапе или подтягиваться из сгенерированных исходников.
Есть документация. Может, в doxygen/аналоге. Может, отдельно. Но корректность этой документации и соответствие кода описанию в ней — вопрос качества сопровождения кода.
Мы в очень многом полагаемся на это качество, начиная вообще с качества выполнения команд процессором. Я не вижу тут пути кроме как "доверяй, но проверяй". Разумеется, если есть методы автоматизированной верификации — их не стоит избегать.
C>P.S. Да, я последнее время как-то увлёкся темой mission critical, формальными доказательствами и прочей, неинтересной в нормальном мире ерундой. Но это не делает мои аргументы бессмысленными. Почитайте, хотя бы, блог PVS-Studio и ужаснитесь количеству глупых ошибок в крупных открытых проектах, которые, очевидно, попали в мастер по недоглядке.
Да. И всё равно всё, насколько я представляю себе, проверить можно только в очень ограниченном наборе верифицируемых окружений.
И должен заметить, что я не понимаю, как это связано с исходной темой.
Здравствуйте, Андрей Тарасевич, Вы писали:
5M>>B.Stroustrup “Subscripts and sizes should be signed”: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1428r0.pdf
АТ>Это один из хороших примеров "Страуструпа несущего чушь". К сожалению, под видом натягивания этой знаковой совы на глобус `std::span` он одновременно пытается натянуть эту сову на весь язык.
Он совершенно правильно ставит проблему. Плохо только то, что любое качественное решение проблемы означает новый язык без обратной совместимости
Здравствуйте, netch80, Вы писали:
AT>> Приведение от unsigned к signed всегда было implementation defined, но сейчас стандарты С и С++ четко определяют и его тоже.
N>И где такое сказано для C?
Я даже удивился, что для придирки к этому утверждению понадобилось столько времени Ждал.
Все, я думаю, поняли, что под "сейчас" я имею в виду то, что отказ от формальной поддержки 1's-complement и sign-and-magnitude платформ — вопрос уже решенный и согласованный между С и С++. Просто считайте, что эта часть уже опубликованного стандарта С++ распространяется и на С тоже.
Здравствуйте, Андрей Тарасевич, Вы писали:
AT>>> Приведение от unsigned к signed всегда было implementation defined, но сейчас стандарты С и С++ четко определяют и его тоже.
N>>И где такое сказано для C?
АТ>Я даже удивился, что для придирки к этому утверждению понадобилось столько времени Ждал.
Больше времени, чем ожидалось — ни о чём. Меньше — может что-то показать, больше — нет.
АТ>Все, я думаю, поняли, что под "сейчас" я имею в виду то, что отказ от формальной поддержки 1's-complement и sign-and-magnitude платформ — вопрос уже решенный и согласованный между С и С++. Просто считайте, что эта часть уже опубликованного стандарта С++ распространяется и на С тоже.
Позиция понятна и с таким пояснением о планах и согласованиях я безусловно согласен. Тем не менее, поскольку дословно сказано было о "стандартах" и "чётко определяют", то утверждение неверно.
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, netch80, Вы писали:
N>>Я не согласен, что в этом случае вообще в этот код надо как-то входить. Задача, где пропускается последний элемент, при пустом массиве означает (практически наверняка) вообще некорректную постановку, если этот массив законный, или нарушение в данных. Мой комментарий был исключительно об этом.
TB>Ну, это не так. Взять тот же случай, когда надо перебрать пары элементов.
Хм, ну предположим. Хотя я с ходу не вижу вариантов применения кроме, простите, сортировки пузырьком — случай редкий и почти бесполезный. Или проверки сортированности массива (зачем?) — то же самое, вид сбоку. Для них, да, пустой массив — законный вариант.
Тогда таки можно считать не [0..len-1), а [1..len) (хм, Marty по сути предлагал то же самое?)
В любом случае, повторюсь, я поддерживаю, что в общем и целом со знаковыми удобнее хотя бы тем, что не надо следить за возможными проблемами перехода 0 -> 0-1. Вопрос был в конкретном примере.
Здравствуйте, netch80, Вы писали:
N>Тогда таки можно считать не [0..len-1), а [1..len) (хм, Marty по сути предлагал то же самое?)
Да, можно просто переписать перебор таким способом. А если надо обойти с конца, то надо просто переписать перебор ещё другим способом. А если надо что-то ещё сделать, то можно просто переписать алгоритм ещё каким-то способом. А если нужен вот такой вот алгоритм, то для него можно просто переписать не так, а вот так. А если нужен такой случае, то можно просто переписать ещё как-то вот так.
И всё это говноедство обусловлено не реальной причиной, а абсолютно надуманной. Просто кто-то не подумал головой.
N>В любом случае, повторюсь, я поддерживаю, что в общем и целом со знаковыми удобнее хотя бы тем, что не надо следить за возможными проблемами перехода 0 -> 0-1. Вопрос был в конкретном примере.
Ну может перебор отрезков ломаной, заданной в виде массива опорных точек, я не говорю, что вариант офигеть какой жизненный.
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Она распространяется не только на беззнаковые типы, но и на указательную арифметику, на итераторы, на потоки и на массу других концепций боле высокого уровня.
И как же сложить два итератора? Или два потока?
Индекс можно являться участником арифметических выражений (и является), а итератор, ну разве что в a-b, и то только для векторов.
Здравствуйте, netch80, Вы писали:
N>Есть документация. Может, в doxygen/аналоге. Может, отдельно. Но корректность этой документации и соответствие кода описанию в ней — вопрос качества сопровождения кода.
N>Мы в очень многом полагаемся на это качество, начиная вообще с качества выполнения команд процессором. Я не вижу тут пути кроме как "доверяй, но проверяй". Разумеется, если есть методы автоматизированной верификации — их не стоит избегать.
Да, так и делается сейчас, но я этого не понимаю. Ведь куда как легче проверять код типа
Здравствуйте, cppguard, Вы писали:
N>>Мы в очень многом полагаемся на это качество, начиная вообще с качества выполнения команд процессором. Я не вижу тут пути кроме как "доверяй, но проверяй". Разумеется, если есть методы автоматизированной верификации — их не стоит избегать.
C>Да, так и делается сейчас, но я этого не понимаю. Ведь куда как легче проверять код типа C>
Ну а я не понимаю, что вам непонятно. Какая принципиальная разница между nread != CONST1 и nread < CONST2, если в каждом из этих вариантов сказано, что такая константа единственная, которая означает ошибку, и CONST2 больше всех не-ошибочных значений?
Проверки полностью эквивалентны.
Здравствуйте, netch80, Вы писали:
N>Ну а я не понимаю, что вам непонятно. Какая принципиальная разница между nread != CONST1 и nread < CONST2, если в каждом из этих вариантов сказано, что такая константа единственная, которая означает ошибку, и CONST2 больше всех не-ошибочных значений? N>Проверки полностью эквивалентны.
Разница в том, что в варианте с != если по какой-то причине кроме -1 функция может вернуть -2, то программа будет содержать ошибку. Ещё хуже то, что это неуправляемая память, и ошибка может проявиться как угодно: аварийное завершение, порча стека и т.д. Если же мы проверяем через <, то мы поделили область определения функции на два диапазона. Математически эта функция становится составной функцией вида
{ f(x), x < X; g(x), X <= x; x э Z_uint32 }
и при этом остаётся total function (я не знаю, как по-русски), т.е. определена на всём промежутке значений входного аргумента.
C !=, конечно же, можно добиться аналогичного эффекта, но нужно тогда писать "x < 0", но так мало кто делает, это заметно даже по комментариям.
Благодарю за хороший, полный ответ, но не согласен с
5M>В 99.99% случаев когда ты размышляешь об операциях с числами, ты и читатели твоего кода подразумевают обычную, а не модульную арифметику
Для меня это не так. Я программист, я пишу программы, программы выполняются на ЭВМ, в ЭВМ целые числа это кольцо по модулю. Скорее, в обратном направлении не всегда удобно думать — что можно сделать и не кольцо, если надо. Но в целом это как с вещественными — сразу в голове держишь конечность представления, двоичные дроби и т.д.
5M>B.Stroustrup “Subscripts and sizes should be signed”: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1428r0.pdf
Ну эт классика — дед, который не пишет production ready код, учит нас жизни. "Круче" него только Скотт Мейерс, который вообще программистом не работал. Но в книгах Скотта хотя бы есть толика логики. Страуструпа я тоже уважаю, но правда жизни такова, что можно сколь угодно теоретизировать, а шишки набиваешь только на практике. Да, я в курсе, что он работал в Морган Стэнлей и, может где-то ещё, но мой опыт говорит, что такие мэтры реальный код не пишут, а больше тусуются в каких-нибудь XXX-Core или YYY-Foundation командах или вообще только проводят тренинги в компании.
5M>Длинное видео из 2013г где комитет C++ несколько раз признает ошибочность использования unsigned: https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything (12:10, 42:40, 1:02:50)
Не открывается =(
Я, кстати, больше пишу на Java, чем на Си и С++, и там, как бы, не из чего выбирать. Но если есть выбор, то почему бы им не воспользоваться? Если бы всё так было очевидно, то unsigned давно бы стал deprecated.
Здравствуйте, cppguard, Вы писали:
N>>Ну а я не понимаю, что вам непонятно. Какая принципиальная разница между nread != CONST1 и nread < CONST2, если в каждом из этих вариантов сказано, что такая константа единственная, которая означает ошибку, и CONST2 больше всех не-ошибочных значений? N>>Проверки полностью эквивалентны.
C>Разница в том, что в варианте с != если по какой-то причине кроме -1 функция может вернуть -2, то программа будет содержать ошибку.
Почему бы ей такое возвращать, если согласно контракту она может или >=0 (что-то полезное) или -1?
Если уж играться с тем, что зовётся defensive programming, то надо всегда проверять корректность результата вызываемой функции — как минимум, сразу писать if (rc < 0 && rc != -1) { взорваться нафиг; }. Ну и аналогично для прочих ситуаций.
Формально к этому можно даже найти обоснования. Например, у большинства линуксовых сисколлов такая обработка (псевдокод, оригинал на ассемблере):
В результате, если вызываемое вернуло (int64_t)(-8000) или что-то похожее, оно не будет опознано как код ошибки — и сохранено, например, как файловый дескриптор, если звалась open().
НО: чем данная ситуация отличается от 100500+ других нарушений — например, просто ошибочно другой дескриптор, неоткрытый файл, открытие не того пути, открытие с неправильными правами, и так далее? Всё это, если происходит, это "просто" ошибки реализации.
C> Ещё хуже то, что это неуправляемая память, и ошибка может проявиться как угодно: аварийное завершение, порча стека и т.д.
Ну вообще-то это не память, это другой ресурс (почти всегда). Например, файловый дескриптор. Хотя не так принципиально.
C> Если же мы проверяем через <, то мы поделили область определения функции на два диапазона. Математически эта функция становится составной функцией вида
C>{ f(x), x < X; g(x), X <= x; x э Z_uint32 }
Не знаю, какой у вас браузер, но ∈ это U+2208. Я стартую такой набор через Ctrl+Shift+U (свойство GTK). Под виндой есть свои методы.
C>и при этом остаётся total function (я не знаю, как по-русски), т.е. определена на всём промежутке значений входного аргумента.
В этих обозначениях, прямой результат функций типа open(), read() и так далее, является [0..INT_MAX] ∪ { -1 }.
Значения в диапазоне [INT_MIN..-2] невозможны.
И снова: если вы полагаетесь на корректность возврата такой функции, то проверки на x<0 и x==-1 эквивалентны. Если не полагаетесь, то надо проверять и случай заведомо некорректного возвращаемого значения.
C>C !=, конечно же, можно добиться аналогичного эффекта, но нужно тогда писать "x < 0", но так мало кто делает, это заметно даже по комментариям.
Я иногда так пишу. И всегда сомневаюсь, правильно ли это.
На x86 вариант "<0" компактнее на несколько байт. Обычно это не критично.
Здравствуйте, cppguard, Вы писали:
C>Благодарю за хороший, полный ответ, но не согласен с
5M>>В 99.99% случаев когда ты размышляешь об операциях с числами, ты и читатели твоего кода подразумевают обычную, а не модульную арифметику
C>Для меня это не так. Я программист, я пишу программы, программы выполняются на ЭВМ, в ЭВМ целые числа это кольцо по модулю.
Думаю, что опыт, который он набрал в процессе разработки начальных версий C++, позволяет ему делать предложения для комитета, которые стоит как минимум послушать. И почему вы думаете, что сейчас он ничего не пишет в продуктин?
5M>>Длинное видео из 2013г где комитет C++ несколько раз признает ошибочность использования unsigned: https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything (12:10, 42:40, 1:02:50)
C>Не открывается =(
Оно есть на ютубе. Задумчиво смотрю сейчас в перерывах работы.
C>Я, кстати, больше пишу на Java, чем на Си и С++, и там, как бы, не из чего выбирать. Но если есть выбор, то почему бы им не воспользоваться? Если бы всё так было очевидно, то unsigned давно бы стал deprecated.
Ну вот то, что вы больше пишете на Java (и не только вы, а реально на Java в разы больше), показывает, что депрекация тут происходит несколько иными путями...
может требовать (согласно утверждённым методикам) написания дополнительного теста в котором my_read возвращает значение меньше -1. А если my_read никогда не возвращает такого, то тест никогда не будет признан пройденным => код не валиден, так как не протестирован.
Здравствуйте, B0FEE664, Вы писали:
BFE>может требовать (согласно утверждённым методикам) написания дополнительного теста в котором my_read возвращает значение меньше -1. А если my_read никогда не возвращает такого, то тест никогда не будет признан пройденным => код не валиден, так как не протестирован.
А что мешает использовать в тесте заглушку, которая вернёт нужное значение?
Здравствуйте, cppguard, Вы писали:
BFE>>может требовать (согласно утверждённым методикам) написания дополнительного теста в котором my_read возвращает значение меньше -1. А если my_read никогда не возвращает такого, то тест никогда не будет признан пройденным => код не валиден, так как не протестирован.
C>А что мешает использовать в тесте заглушку, которая вернёт нужное значение?
Ничего: нужно написать ещё один специальный тест, описать что он тестирует, добавить в список исполняемых тестов... Затягивание сроков может быть выгодно, но приветствуется не всегда.
Здравствуйте, kov_serg, Вы писали:
_>Всё очень просто 0-нет ошибки, не 0 — есть ошибка и всё.
Ну как так? А количество прочитанных байт? А результат нетривиальной успешной операции?
Здравствуйте, Kernan, Вы писали: K>Здравствуйте, kov_serg, Вы писали: _>>Всё очень просто 0-нет ошибки, не 0 — есть ошибка и всё. K>Ну как так? А количество прочитанных байт? А результат нетривиальной успешной операции?
Вас что именно интересует?
int fn(fn_output_t *output,fn_input_t *input); // 0-нет ошибок, не 0 код ошибки
В частности
int my_read2(void *ctx,void *buffer,int size,int *readed) {
int rd=my_read(ctx,buffer,size);
if (readed) *readed=rd;
return rd<0 ? rd : 0;
}
{
int rc=0;
...
int buf_size;
if (my_read2(stream,buf,buf_lim,&buf_size)) { rc=my_error_read_problem; goto leave; }
...
leave:
if (stream) my_close(stream);
...
return rc;
}
Скрытый текст
Или помещать результат вместе с кодами ошибок в структуру результата либо использовать std::tuple
Здравствуйте, cppguard, Вы писали:
C>Ну эт классика — дед, который не пишет production ready код, учит нас жизни
Для меня классикой уже стало наблюдать, как русские программисты выходят из тайги, отрицают все источники и авторитеты, и делают по-своему просто чтобы почувствовать себя чуть умнее, чем на самом деле. Откуда у нас столько одаренных людей берется, которые лучше знают что нужно дизайну языка, чем дизайнеры языка, ума не приложу
Он не учит вас жизни, а приводит аргументы, что знаковые были бы для языка лучше. Есть контраргументы — высказывайте... Или в универе больше не учат, что обсуждение личности человека не является опровержением его аргументов? Почему я в англоязычных дискуссиях подобного невежества не наблюдаю?
C>Я, кстати, больше пишу на Java, чем на Си и С++, и там, как бы, не из чего выбирать.
Автор Java отказался включать в язык беззнаковые типы по той же самой причине:
> Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand what goes on with unsigned, what unsigned arithmetic is. Things like that made C complex. The language part of Java is, I think, pretty simple.
Здравствуйте, cppguard, Вы писали:
5M>>Для меня классикой уже стало наблюдать, как русские программисты выходят из тайги, отрицают все источники и авторитеты, и делают по-своему просто чтобы почувствовать себя чуть умнее, чем на самом деле. Откуда у нас столько одаренных людей берется, которые лучше знают что нужно дизайну языка, чем дизайнеры языка, ума не приложу
C>Страуструп создатель, а не дизайнер, дизайнер это коммитет.
Создатель, главный разработчик в течение многих лет (пока не пришлось действительно всё переложить на комитет), и один из дизайнеров (дизайнеры — было во множественном числе).
Разумеется, это само по себе не признак безусловной правоты, но, в отличие от любого из нас, кто видит от силы 10% языка, люди в комитете видят все варианты применения (вплоть до самых извращённых), получают массу сведений о проблемах, до которых крайне сложно самому додуматься, и так далее.
А ещё авторы языковых средств при реализации, просто от того, что они пишут компилятор, натыкаются на массу вопросов, как решить конкретный момент, и за счёт этого понимают язык сильно лучше того, кто на нём пишет.
C> Точно так же, как Java сейчас разрабатывает Оракл, а не Гослинг. Авторитеты в инженерном деле это идолопоклонничество. А людям из тайги своё мнение не положено, да?
Люди из тайги, повторю мысль, видят только тайгу. Человек из тайги может думать, что все едят оленей и кедровые орехи.
5M>>Он не учит вас жизни, а приводит аргументы, что знаковые были бы для языка лучше. Есть контраргументы — высказывайте... Или в универе больше не учат, что обсуждение личности человека не является опровержением его аргументов? Почему я в англоязычных дискуссиях подобного невежества не наблюдаю?
C>Потому что все нетоксичные, предельно вежливые и прочее, так заведено. Я видел обсуждения внутренних групп FAANG касательно всяких священных войн, и С++ в то числе, так там яд разве что через монитор не брызгал, но внешне, особенно для ex-USSR населения, всё выглядело чинно-благородно.
Психологический факт, что если (пусть даже насильно заставленные) люди высказываются в пределах корректности, это позволяет лучше обдумать позицию, и в итоге смягчить её, избавив от бессмысленного радикализма. Это не на 100% полезно, но конструктивная часть "нетоксичности" именно в этом.
C> А самое непрятное в таких обсуждениях то, что очень быстро от фактов и логических умозаключений переходят к "the results of most computations are signed" (слова Страуструпа), то есть полностью субъективному мнению,
Может, оно таки объективно? (см. выше)
C> а мнение это имеет очень большой вес. Вот и приходят в выводу "почему-бы не прислушаться к словам мэтра, он же велик". А код при этом сопровождают простые инженеры.
5M>>В дотнете и тысяче других более-менее новых языков тоже все размеры и индексы знаковые, угадайте почему, вы бы лучше учились чем умничать.
C>Я где-то писал, что индексы должны быть беззнаковые?
Тогда против чего вы возражаете данным сообщением? Страуструп высказывался в первую очередь про индексы и размеры.
Здравствуйте, Андрей Тарасевич, Вы писали:
N>>В любом случае, повторюсь, я поддерживаю, что в общем и целом со знаковыми удобнее хотя бы тем, что не надо следить за возможными проблемами перехода 0 -> 0-1. Вопрос был в конкретном примере.
АТ>Тема умения грамотно работать возле левого края интервалов вида [a, b) в условиях, когда выход за левый край интервала приводит к неожиданному, нежелательному или неопределенному поведению (ваши "проблемы перехода 0 -> 0-1"), является фундаментальным нативным свойством языков С и С++, да и вообще программирования в целом. Она распространяется не только на беззнаковые типы, но и на указательную арифметику, на итераторы, на потоки и на массу других концепций боле высокого уровня.
АТ>Поэтому попытки устранить эту "проблему" в области индексирования массивов — это локальное решение, которое ничего не поменяет в общем. Следить за "возможными проблемами перехода 0 -> 0-1" в более обобщенно понимании вам все равно придется ничуть не меньше чем ранее. Ценность "неудобной" беззнаковой арифметики в индексации заключается в том числе в том, что она с ранних пелёнок оповещает подрастающее поколение о существовании этой темы, с которой им в дальнейшем придется сталкиваться постоянно (независимо от того, какие типы они будут фактически использовать для индексации). Переход на знаковые типы лишь замедлит процесс формирования ценных идиом.
Интересные доводы, я сохранил. Но по отношению к ним есть принципиальные проблемы: в первую очередь этот как раз состоявшийся по факту "алиасинг" modulo-типа не вполне корректным словом unsigned (о чём тут вспоминал коллега σ). "Формирование ценных идиом" должно начинаться с этого (и объяснения, в чём авторы стандарта тут подложили каку). А тут уже вопрос обучения: я пока что ни в одном популярном учебнике по C или C++ не видел, чтобы в это явно ткнули носом (а для большинства таки надо ткнуть, я не был исключением).
Ну и в итоге это имеет ценность только до тех пор, пока сохраняется совместимость с данной... мнэээ... особенностью эхотага. Учить тому, что не надо идти, например, от begin() на шаг назад, это универсально.
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, netch80, Вы писали:
TB>Я пишу на Расте и проблема никуда не делась. TB>Обычный заголовок
TB>
TB>for i in 0 .. v.len()-1
TB>
уж что-что, но это не обычный заголовок. В данном случае .. в терминах математики означает [0; v.len()-1). Последний элемент не включается. То есть вы ещё один элемент откусили от вектора.
так ошибки нет вовсе:
for i in 0 .. v.len()
конечно можно постараться и таки выстрелить в ногу:
for i in 0 ..= v.len() - 1
А такой код уже работает:
for i in 0 ..= v.len() as i8 - 1
Вообще циклы добавили в раст для выявления профнепригодности. Итераторы же есть.
Здравствуйте, netch80, Вы писали:
N>Разумеется, это само по себе не признак безусловной правоты, но, в отличие от любого из нас, кто видит от силы 10% языка, люди в комитете видят все варианты применения (вплоть до самых извращённых), получают массу сведений о проблемах, до которых крайне сложно самому додуматься, и так далее.
Есть разница в том, чтобы видеть различные примеры использования языка и понимать на практике сильные и слабые стороны.
N>Люди из тайги, повторю мысль, видят только тайгу. Человек из тайги может думать, что все едят оленей и кедровые орехи.
А люди из Сан-Франциско — только бомжей и ссанину, и что? Я тут вашу мысль не понял. Мысль предыдущего оратора понятна — "куда в со своим свиным рылом в калашный ряд, ещё иметь своё мнение посмели". И в предложении "дед учит жизни" нет ничего токсичного. Я даже на личности не переходил. Страуструп действительно дед, а тон его статьи вкупе с тем, что никаких примеров из реальной жизни (рабочий код, открытые проекты) он не привёл, позволяет использоваться оборот "учить жизни", который оскорбительным в целом и не является.
N>Может, оно таки объективно? (см. выше)
Как могут быть объективными слова "the most of computations" без приведения статистики? Или Страуструп у нас уже Чак Норрис и видел весь код на свете, два раза? Аналогично можно сказать "the most of integer additions overflow, so let's make it long long by default".
N>Тогда против чего вы возражаете данным сообщением? Страуструп высказывался в первую очередь про индексы и размеры.
Против его аргументов. И не возражаю, а просто высказал своё мнение насчёт ценности статьи. Но знал бы, что комментарий породит столько флуда, не стал бы писать, чтобы не увеличивать мировую энтропию =) Предлагаю на этом закончить обсуждение в этой ветке. Я получил комментарии, было очень интересно узнать новое, есть над чем подумать.
TB>Является валидным полностью рабочим кодом для знакового индекса. Но в Расте этот заголовок падает на пустом векторе. В С++ я бы получил ассерт при обращении по инвалидному индексу.
Я, не зная языка rust, а просто исходя из записи ожидаю, что при пустом v индекс i будет принимать значения [0, -1). Если предположить, что для диапазона [0, -2) i будет принимать значения {0, -1}, то для диапазона [0, -1) индекс i будет принимать значение {0}.
TB>Внимание вопрос: а какая мне нахрен разница, что программа упала не с дурацким старомодным отсталым ассертом, а с красивой модной прогрессивной молодёжной паникой? TB>Так что нет, индексы и размеры должны быть знаковыми и только знаковыми.
Мне не понятно, причём тут знаковость. В rust нельзя писать так: for i in 0 .. -8 ?
Здравствуйте, cppguard, Вы писали: C>Как могут быть объективными слова "the most of computations" без приведения статистики? Или Страуструп у нас уже Чак Норрис и видел весь код на свете, два раза? Аналогично можно сказать "the most of integer additions overflow, so let's make it long long by default".
Страуструп имел в виду, что первое же вычитание делает вычисление знаковым. Ну, потому, что очень мало кто ожидает при вычислении (5 — 8) получить 18446744073709551613, а не -3.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, netch80, Вы писали:
M>>Поэтому надо просто писать M>>
M>>for(auto i=0u; i!=v.size(); ++i)
M>>
N>А теперь повторите это для прохождения по элементам в порядке убывания индексов.
for(auto i = v.size()-1; i != -1; --i)
...
Обоснование:
7.4 Usual arithmetic conversions
Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield
result types in a similar way.
...
— Otherwise, the integral promotions (7.3.6) shall be performed on both operands.56 Then the following rules shall be applied to the promoted operands:
...
(1.5.3) — Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the
rank of the type of the other operand, the operand with signed integer type shall be converted to
the type of the operand with unsigned integer type.
Так что -1 конвертируется в unsigned integer type.
Согласно 6.8.1 Fundamental types
An unsigned integer type has the same width N as the corresponding signed integer type. The range of representable values for the unsigned type is 0 to 2N − 1 (inclusive); arithmetic for the unsigned type is performed modulo 2N.
Given an integer n > 1, called a modulus, two integers a and b are said to be congruent modulo n, if n is a divisor of their difference (i.e., if there is an integer k such that a − b = kn).
Для нас n = 2N. Математически число -1 сравнимо по модулю с числом (2N − 1), так как 2N − 1 — (-1) = 2N, а 2N делится на 2N без остатка. Следовательно -1 будет конвертировано в 2N−1 и цикл отработает как надо.
Здравствуйте, B0FEE664, Вы писали:
BFE>Я, не зная языка rust, а просто исходя из записи ожидаю, что при пустом v индекс i будет принимать значения [0, -1). Если предположить, что для диапазона [0, -2) i будет принимать значения {0, -1}, то для диапазона [0, -1) индекс i будет принимать значение {0}.
Ээээ, вообще-то [0, -1), и [0, -2) тоже — это пустой диапазон
BFE>Мне не понятно, причём тут знаковость. В rust нельзя писать так: for i in 0 .. -8 ?
Можно, только v.len() имеет беззнаковый тип. Можно вручную скастить, только какого хрена я должен засирать код этим бойлерплейтом из-за чьей-то придури?
Я пытаюсь показать, что между частями его сообщения нет никакой логической связи. Делаю я это, меняя посылку на противоположную, демонстрируя, что при этом ничего по сути не меняется.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, cppguard, Вы писали: C>>Как могут быть объективными слова "the most of computations" без приведения статистики? Или Страуструп у нас уже Чак Норрис и видел весь код на свете, два раза? Аналогично можно сказать "the most of integer additions overflow, so let's make it long long by default". S>Страуструп имел в виду, что первое же вычитание делает вычисление знаковым. Ну, потому, что очень мало кто ожидает при вычислении (5 — 8) получить 18446744073709551613, а не -3.
Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости.
Страуструп говорит совсем другое. Страуструп говорит, что в ситуации, когда "в аудитории было 5 студентов, затем пришли еще 3, а затем ушли 8" безалаберный Джонни может начать с вычитания 5 — 8 и получения пугающего Джонни результата (даже если в итоге он приведет к правильному ответу). И чтобы программирование стало менее пугающим для таких безалаберных Джонни, лучше сразу работать в знаковой арифметике.
Здравствуйте, B0FEE664, Вы писали:
N>>А теперь повторите это для прохождения по элементам в порядке убывания индексов.
BFE>
BFE>for(auto i = v.size()-1; i != -1; --i)
BFE>...
BFE>
Это — правильно, но содержит другой подводный камень. Если вдруг окажется, что `size()` возвращает "маленький" беззнаковый тип, т.е. тип подпадающий под integral promotions, то цикл снова получится бесконечным.
Идиоматический код для обратной итерации до нуля прекрасно известен
for (auto i = v.size(); i > 0; )
{
--i;
//...
}
или, если вы предпочитаете
for (auto i = v.size(); i-- > 0; )
{
//...
}
Все.
Эта идиома, еще раз, пригодится не только при работе с беззнаковой арифметикой, но и, как я уже говорил выше, для обратной итерации по любым интервалам, выход за левую границу которых недопустим: интервалы итераторов, интервалы указателей, и т.п.
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>>Она распространяется не только на беззнаковые типы, но и на указательную арифметику, на итераторы, на потоки и на массу других концепций боле высокого уровня.
TB>И как же сложить два итератора? Или два потока? TB>Индекс можно являться участником арифметических выражений (и является), а итератор, ну разве что в a-b, и то только для векторов.
Это не имеет никакого отношения к рассматриваемой теме.
К рассматриваемой теме имеет отношение то, что для ряда дискретных "итераторных" сущностей диапазоны вида [a, b) невозможно тривиальным образом превратить в обратные диапазоны [b-1, a-1) по той причине, что выражение `a-1` либо вообще не определено, либо дает "неправильный" результат. Это относится и к беззнаковой индексации/позиционированию, и к итераторам/указателям и еще очень много к чему. В этом и состоит суть рассматриваемой проблемы.
Здравствуйте, Андрей Тарасевич, Вы писали:
S>>Страуструп имел в виду, что первое же вычитание делает вычисление знаковым. Ну, потому, что очень мало кто ожидает при вычислении (5 — 8) получить 18446744073709551613, а не -3.
АТ>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости.
Что за жёлтая муть?
В мире существует множество разных понятий, которые можно свести к числам, и, например, векторов (включая противоположно направленные) в нём не меньше, чем чисел.
И даже количество материи может быть отрицательным (временно, на квантовом уровне).
АТ>Страуструп говорит совсем другое. Страуструп говорит, что в ситуации, когда "в аудитории было 5 студентов, затем пришли еще 3, а затем ушли 8" безалаберный Джонни может начать с вычитания 5 — 8 и получения пугающего Джонни результата (даже если в итоге он приведет к правильному ответу). И чтобы программирование стало менее пугающим для таких безалаберных Джонни, лучше сразу работать в знаковой арифметике.
Проблема, да, в самой операции вычитания и определения корректности промежуточных значений. Не бывает отрицательного количества студентов, редко бывает в чистом виде отрицательных денег (случай кредита под 0% это всё равно кредит), и так далее — и что, от этого не пользоваться этими числами, когда это не нарушает корректность модели?
Использование знаковых тут позволяет оставаться в пределах действительно понятных на любом шаге чисел.
Но в таких операциях проще понять (обычно), если вылезешь в минус.
А вот отсутствие for с постпроверкой, например, приводит к грустным костылям.
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>К рассматриваемой теме имеет отношение то, что для ряда дискретных "итераторных" сущностей диапазоны вида [a, b) невозможно тривиальным образом превратить в обратные диапазоны [b-1, a-1) по той причине, что выражение `a-1` либо вообще не определено, либо дает "неправильный" результат. Это относится и к беззнаковой индексации/позиционированию, и к итераторам/указателям и еще очень много к чему. В этом и состоит суть рассматриваемой проблемы.
Значит для итераторов арифметика не работает, только и всего.
Это не повод портить арифметику индексов
Здравствуйте, cppguard, Вы писали:
N>>Разумеется, это само по себе не признак безусловной правоты, но, в отличие от любого из нас, кто видит от силы 10% языка, люди в комитете видят все варианты применения (вплоть до самых извращённых), получают массу сведений о проблемах, до которых крайне сложно самому додуматься, и так далее.
C>Есть разница в том, чтобы видеть различные примеры использования языка и понимать на практике сильные и слабые стороны.
И вы всерьёз считаете, что человек, который пусть сам пишет, но один частный вариант, будет лучше понимать тонкости, чем тот, кто пусть пишет примерчики и мелкие тесты, но зато видит от других огромное количество вариантов, о которых бы никогда не додумался?
N>>Люди из тайги, повторю мысль, видят только тайгу. Человек из тайги может думать, что все едят оленей и кедровые орехи. C>А люди из Сан-Франциско — только бомжей и ссанину, и что? Я тут вашу мысль не понял.
См. выше.
C> Страуструп действительно дед, а тон его статьи вкупе с тем, что никаких примеров из реальной жизни (рабочий код, открытые проекты) он не привёл,
Примеры, я уверен, специально подобраны так, чтобы отображать конкретную идею на минимальном количестве символов. Для этого им не нужно быть выдиркой реального кода, скорее наоборот.
C> позволяет использоваться оборот "учить жизни", который оскорбительным в целом и не является.
Но всё отношение к нему — было однозначно уничижительным.
N>>Может, оно таки объективно? (см. выше) C>Как могут быть объективными слова "the most of computations" без приведения статистики? Или Страуструп у нас уже Чак Норрис и видел весь код на свете, два раза? Аналогично можно сказать "the most of integer additions overflow, so let's make it long long by default".
long long тоже переполнится
Статистика тут, безусловно, будет врать без уточнения разделения по крупным сродственным группам (типа, в среднем человек имеет одну грудь), но про источники можно было попытаться спросить напрямую. Вообще, ACM и IEEE (как минимум) периодически собирают такие данные.
N>>Тогда против чего вы возражаете данным сообщением? Страуструп высказывался в первую очередь про индексы и размеры.
C>Против его аргументов. И не возражаю, а просто высказал своё мнение насчёт ценности статьи. Но знал бы, что комментарий породит столько флуда, не стал бы писать, чтобы не увеличивать мировую энтропию =) Предлагаю на этом закончить обсуждение в этой ветке. Я получил комментарии, было очень интересно узнать новое, есть над чем подумать.
Я тоже. И продолжу поддерживать как знаковость по умолчанию, так и мастдайность эхотага с заменой на что-то приличное как можно быстрее
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, kov_serg, Вы писали:
_>>Тролите
TB>Я пытаюсь показать, что между частями его сообщения нет никакой логической связи. Делаю я это, меняя посылку на противоположную, демонстрируя, что при этом ничего по сути не меняется.
просто unsigned почти всегда >=0, (кроме случаев с "умным" компилятором и неопределенной переменной)
Здравствуйте, netch80, Вы писали:
N>И вы всерьёз считаете, что человек, который пусть сам пишет, но один частный вариант, будет лучше понимать тонкости, чем тот, кто пусть пишет примерчики и мелкие тесты, но зато видит от других огромное количество вариантов, о которых бы никогда не додумался?
Я, честно, не вижу смысла в этом споре. Просто для справки: коммитет (со Страуструпом среди прочих) подарили нам мёртворождённый auto_ptr и string, который сначала copy-on-write, а потом never copy-on-write. Это, конечно же, они сделали на основе своего глупокого и многолетнего опыта разработки.
N>Но всё отношение к нему — было однозначно уничижительным.
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости.
И каков же в нашем мире результат вычитания 8 из 5?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, cppguard, Вы писали:
N>>И вы всерьёз считаете, что человек, который пусть сам пишет, но один частный вариант, будет лучше понимать тонкости, чем тот, кто пусть пишет примерчики и мелкие тесты, но зато видит от других огромное количество вариантов, о которых бы никогда не додумался?
C>Я, честно, не вижу смысла в этом споре. Просто для справки: коммитет (со Страуструпом среди прочих) подарили нам мёртворождённый auto_ptr и string, который сначала copy-on-write, а потом never copy-on-write. Это, конечно же, они сделали на основе своего глупокого и многолетнего опыта разработки.
"глупокий" — хорошее слово. Случайно или намеренно?
В случае string, я подозреваю, это ориентация на типовые свойства процессоров. За ~20 лет, например, важность кэширования и проблем с ним возросла не просто — с минимума до всепоглощающего уровня.
Похожая реформа прошла и в Java — JVMы перешли на не-COW строки в районе Java 8.
С auto_ptr — я пропустил период его значимости (я плотно вернулся к C++ в районе 2014) и не знаю, какие там фундаментальные проблемы. cppreference.com описывает по сути только одно: у него копирование работает как перемещение. С учётом того, что с 1988 по 2011 не могли стандартизовать семантику перемещения, проблема сидела где-то глубже — в районе принципиальной способности комитета принять решения — но что мешало тогда, честно, облом вкапываться, это совсем другая проблема.
Тем не менее, отдельные ляпы не дают причин сомневаться в неработоспособности механизма узнавания фундаментальных проблем в целом.
N>>Но всё отношение к нему — было однозначно уничижительным. C>Отнюдь. Максимум — шутливое.
Здравствуйте, Андрей Тарасевич, Вы писали:
N>>>А теперь повторите это для прохождения по элементам в порядке убывания индексов. BFE>>
BFE>>for(auto i = v.size()-1; i != -1; --i)
BFE>>...
BFE>>
АТ>Это — правильно, но содержит другой подводный камень. Если вдруг окажется, что `size()` возвращает "маленький" беззнаковый тип, т.е. тип подпадающий под integral promotions, то цикл снова получится бесконечным.
Если писать с нулевой терпимостью к предупреждениям, то тогда и в этом случае проблем не будет:
for(auto i = v.size()-1; i != static_cast<decltype(i)>(-1); --i)
АТ>Идиоматический код для обратной итерации до нуля прекрасно известен
Да, он состоит в том, чтобы использовать итераторы в связке с std:/std::ranges::for_each
Индексы редко нужны.
Здравствуйте, Sinclair, Вы писали:
АТ>>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости. S>И каков же в нашем мире результат вычитания 8 из 5?
В реальном мире = 0. С оговоркой, что 0 — это пусто.
В мире идей = -3.
Выберите свой мир.
Здравствуйте, netch80, Вы писали:
АТ>>>>Нет. Кардинальные (количественные) вычисления всегда беззнаковы, сколько бы вы ни занимались вычитаниями. Наш мир принципиально беззнаков по отношению к количественным величинам. И становиться "знаковым" из-за вычитания ему нет никакой необходимости. S>>>И каков же в нашем мире результат вычитания 8 из 5? BFE>>В реальном мире = 0. С оговоркой, что 0 — это пусто. N>Точно не ноль. Скорее — исключение по невозможности совершить операцию.
Исключение в реальном мире? Это как? Допустим вы полезли в холодильник с целью достать оттуда 8 куриных яиц, а там только 5. И тут случилось "исключение по невозможности совершить операцию". Что это может быть?
BFE>>В мире идей = -3. BFE>>Выберите свой мир. N>Ваша вселенная удивительна, но людям там слегка не место.
В современном мире реалисты встречаются редко: одни в бога верят, другие — в отрицательные числа...
Здравствуйте, B0FEE664, Вы писали:
S>>И каков же в нашем мире результат вычитания 8 из 5? BFE>В реальном мире = 0. С оговоркой, что 0 — это пусто. BFE>В мире идей = -3.
В реальном мире нет ни 8, ни 5 — это всё абстракции, придуманные человеком для универсального описания реальности. Вот ты смог этими числами описать, например, число яиц в холодильнике. Также можно описать число яблок или вершин в многоугольнике. В реальном мире нет математики в принципе.
Здравствуйте, Nuzhny, Вы писали:
S>>>И каков же в нашем мире результат вычитания 8 из 5? BFE>>В реальном мире = 0. С оговоркой, что 0 — это пусто. BFE>>В мире идей = -3. N>В реальном мире нет ни 8, ни 5 — это всё абстракции, придуманные человеком для универсального описания реальности.
Ну это тоже перегиб. Понятно, что все яйца разные, но с какой-то погрешностью можно обобщить их до одинаковости, особенно если одного типа из одной партии. Точно так же и в принципах типа "взрослому амурскому тигру зимой надо 20 кг мяса в сутки" (неважно, точная цифра или нет, но можно считать животных в этом смысле примерно эквивалентными).
Масса материи универсальна с хорошей точностью (релятивистские эффекты нам обычно неактуальны, и ядерный реактор не обсчитываем). И так далее.
N> Вот ты смог этими числами описать, например, число яиц в холодильнике. Также можно описать число яблок или вершин в многоугольнике. В реальном мире нет математики в принципе.
Есть. Но реализуется обычно иначе и сложнее. E=m*c**2, законы Ньютона, уравнения Максвелла и 100500 прочих аналогичных законов.
И в них и отрицательные числа сплошь и рядом, и сложение векторов разных знаков...
А вот целые и особенно рациональные числа, если это не коэффициенты или степени — это таки редкость (ну, в квантовом мире таки чаще, там ½ на каждом углу). Основная масса — вещественные, естественно бесконечной (насколько известно) точности.
Здравствуйте, netch80, Вы писали:
N>Ну это тоже перегиб. Понятно, что все яйца разные, но с какой-то погрешностью можно обобщить их до одинаковости, особенно если одного типа из одной партии. Точно так же и в принципах типа "взрослому амурскому тигру зимой надо 20 кг мяса в сутки" (неважно, точная цифра или нет, но можно считать животных в этом смысле примерно эквивалентными).
Я не считаю это перегибом. Если кто-то не видит отрицательных чисел в природе, то почему я должен видеть целые или дроби? Если говорить ро ноль, то это тоже очень не интуитивная штука, его люди изобрели достаточно поздно.
N>Есть. Но реализуется обычно иначе и сложнее. E=m*c**2, законы Ньютона, уравнения Максвелла и 100500 прочих аналогичных законов.
Я же не утверждаю, что самих явлений не существует. Явления существуют и мы описываем наблюдения над ними с помощью абстракций — математики. Сначала придумали натуральные числа, расширили их нулём, додумали дроби, добавили отрицательные, смогли записывать формулы и уравнения. Не вижу разницы между всеми этими абстракциями, кроме как что некоторые более, а другие менее интуитивны. Даже больше: одни и те же явления можно описывать разными абстракциями, например можно сравнить углы Эйлера и кватернионы. Первые интуитивны, вторые не очень, но какая разница?
N>И в них и отрицательные числа сплошь и рядом, и сложение векторов разных знаков...
Абсолютно согласен.
Самое интересное будет, если обнаружится в принципе совсем другая математика, сособная также хорошо описывать природу, но остроенная на абсолютно других ринципах и без всяих натуральных числах и аксиом Пеано.
Здравствуйте, Nuzhny, Вы писали:
S>>>И каков же в нашем мире результат вычитания 8 из 5? BFE>>В реальном мире = 0. С оговоркой, что 0 — это пусто. BFE>>В мире идей = -3.
N>В реальном мире нет ни 8, ни 5 — это всё абстракции, придуманные человеком для универсального описания реальности. Вот ты смог этими числами описать, например, число яиц в холодильнике. Также можно описать число яблок или вершин в многоугольнике. В реальном мире нет математики в принципе.
Продолжая эту вашу мысль легко прийти к тому, что человеческого языка не существует, т.к. это придуманное человеком символическое универсальное описание реальности. Однако язык, как явление, несомненно существует и реален. Другое дело, что язык не ограничен описание только реальных вещей и явлений, но так же позволяет описать то, чего не существует. Так же и с математическими абстракциями — с их помощью можно выразить то, чего не существует, причём не только в реальном мире, но и в мире идей. Например, считается, что в математике не существует противоречий и на основе этого строятся все доказательства «от противного».
Здравствуйте, B0FEE664, Вы писали:
BFE>Продолжая эту вашу мысль легко прийти к тому, что человеческого языка не существует, т.к. это придуманное человеком символическое универсальное описание реальности.
Не вижу, как оно следует.
BFE>Однако язык, как явление, несомненно существует и реален. Другое дело, что язык не ограничен описание только реальных вещей и явлений, но так же позволяет описать то, чего не существует. Так же и с математическими абстракциями — с их помощью можно выразить то, чего не существует, причём не только в реальном мире, но и в мире идей. Например, считается, что в математике не существует противоречий и на основе этого строятся все доказательства «от противного».
Никто так не считает уже последние 100 лет точно, с тех пор как под математику начали подводить аксиоматическое основание.
BFE>Ээээ, вообще-то [0, -1), и [0, -2) тоже — это пустой диапазон
Всё верно пишет. Множество решений системы уравнений (X > 10; X < 2) тоже является пустым. BFE>Отрицание существования отрицательных чисел интуитивно согласуется с этим утверждением.
Интуиция — плохой инструмент для научных и инженерных задач. BFE>Ну, а если вы думаете, что отрицательные числа существуют, то покажите мне минус пять яблок. Именно яблок, а не их огрызков.
Эмм. Я не говорил, что существуют отрицательные яблоки. Но каким образом вы из этого выводите несуществование отрицательных чисел, мне непонятно.
Тем же способом мы легко можем "опровергнуть" существование рациональных чисел: покажите мне половинку электрона. Или одну восьмую.
BFE>На дворе коронавирус, а вы себя за лицо хватаете...
Я привит.
BFE>Числа — это всё условности: BFE>
BFE>for(unsigned i = 2; i != -4; --i)
BFE> std::cout << i << std::endl;
BFE>
BFE>2 BFE>2 BFE>1 BFE>0 BFE>4294967295 BFE>4294967294 BFE>4294967293 BFE>
Совершенно верно. Я правильно понимаю, что вы считаете, что при извлечении из холодильника с пятью яйцами семи яиц в нём останется 4294967294 яйца?
Или вам этот результат кажется интуитивно более корректным, чем -2?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, T4r4sB, Вы писали:
TB>Вот только индексация универсальнее итератора, и оптимизируется лучше.
оно конечно так. Если бы ещё человеки умели делать индексацию без ошибок. Оставьте компьютеру компьютерово. Даже в "обычных заголовках" "пенсионеры" делают ошибки. Что уж говорить про менее тривиальные примеры.
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>В мире количественных вычислений не бывает не только "результата вычитания 8 из 5", но не бывает даже и необходимости вычитать 8 из 5. Возникновение такой необходимости в каких-либо выкладках — это звоночек, говорящий, что где-то раньше вы совершили ошибку и забрели не в ту степь.
Если речь о кардинальных числах, то в их алгебре нет не просто вычитания 8 из 5, там вообще вычитания нет.
О чём я и говорил — введение операции вычитания разрушает замкнутость алгебры, и приходится выйти за пределы неотрицательных чисел.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, T4r4sB, Вы писали:
5M>>В дотнете и тысяче других более-менее новых языков тоже все размеры и индексы знаковые TB>Но комитет Раста решил всё сделать через жопу
Согласен, и самое забавное у них что макс. размер вектора все равно isize::max а не usize::max, все, чего они добились беззнаковостью это жалобы на слишком много кастов при работе с индексами.
Ну они хотя бы соломку попытались подстелить: в Debug режиме и знаковые и беззнаковые обрывают программу при переполнении т.е. арифметика подразумевается нормальная независимо от знаковости, плюс неявные преобразования между знаковыми и беззнаковыми без каста запрещены.
Здравствуйте, 5.März, Вы писали:
5M>Согласен, и самое забавное у них что макс. размер вектора все равно isize::max а не usize::max, все, чего они добились беззнаковостью это жалобы на слишком много кастов при работе с индексами.
А они на эти жалобы как-то реагируют? Ну в лучшем случае хотелось бы "мы признаём ошибку, но менять легаси сложно", а не "вы ничиво напинимаити1111 кхампайл-тхаймовый инвариант ниатрицатильнасти111".