Здравствуйте, Reset, Вы писали:
N>>Но тут проблема не типа как такового — к моменту прихода оптимизаций типы уже стёрты, а диапазона значений. Проверка выбрасывается в правом случае потому, что после конверсии end-beg в беззнаковое оказывается, что в условии немедленного выхода "0 >= (unsigned size_t)(end-beg)" случай "больше" невозможен и его можно не проверять. Но это корректно только до тех пор, пока реально end >= beg! Вы косвенно, за счёт неявной конверсии к беззнаковому в этом сравнении, гарантируете это условие (и получите очень больно, если нарушите его).
R>Не очень понимаю, про какие проверки вы все говорите: godbolt (смотреть на серенькое и желтенькое, т.е. содержимое цикла).
Добавь -m32, или замени int на long, чтобы размер индекса совпадал с разрядностью платформы.
Здравствуйте, Reset, Вы писали:
R>Не очень понимаю, про какие проверки вы все говорите: godbolt (смотреть на серенькое и желтенькое, т.е. содержимое цикла).
Вы не на то смотрите, посмотрите на прошлый пример. В беззнаковом варианте вычитание и сразу je, в знаковом вычитание, test для проверки результата и за ним jle.
У нас есть цикл: for(TYPE i = 0; i < (end — begin); ++i ) { baz(i); } // (в каком примере где foo, bar, baz — мне пофиг, разберётесь по описанию)
Сначала компилятор его разбирает в:
i = 0;
cycle_body_start: if (!(i < (end - begin))) goto cycle_break;
baz(i);
cycle_continue: ++i; goto cycle_body_start;
cycle_break:
а затем — типовая оптимизация сейчас — превращает в:
i = 0;
if (!(i < (end - begin))) goto cycle_break;
cycle_body:
baz(i);
cycle_continue: ++i;
if (i < (end - begin)) goto cycle_body;
cycle_break:
таким образом, условие выхода в этом варианте может быть реализовано дважды, с противоположным финальным знаком (в условии невхода — инвертировано, а продолжения — прямое).
Почему такая оптимизация — с ходу не помню — но, как вариант, из-за branch prediction.
Теперь начинаем оптимизировать. Компилятор знает, что в цикл он вошёл при i=0, затем i только увеличивался, и он может выкинуть в условии продолжения варианты i >= (end-begin) — что он успешно и делает; поэтому в цикле независимо от знака будет проверка одного и того же типа — на границу, зная, что мы к ней подходим снизу. А вот вначале — на входной проверке прямо перед cycle_body — он в случае знакового знает, что 0 может оказаться всё равно не меньше, чем end — begin, ему не гарантировали ничего про эту разность, поэтому вставлены test + jle; а при беззнаковых 0 > end — begin не может быть, поэтому он сокращает >= до ==.
В вашем же примере картина замутнена использованием не long (тип, равный ptrdiff_t и size_t по ширине), а int. Поэтому мышление компилятора сбито уже странными эффектами — например, что, если end — begin переполнит даже uint32_t? Отсюда совершенно ненужный, в норме, jle на входной проверке. Более того, там jl в цикле! Вы совсем запутали бедную программу, она если на вашем unsigned достигнет 2**31... нет, выполнение не нарушится за счёт того, что `mov eax, ebx` косвенно беззнаково расширяет 32->64, но jl вместо jne показывает, что ему поплохело. А вот если signed int, то он делает jne!
Здравствуйте, vopl, Вы писали:
V>>>Да, согласен . На один бит. Но не "гораздо меньше". N>>Так в 2 раза же. V>Ок, в 2 раза, на один бит.
Тут достаточно.
N>>А, на входе... я тело цикла смотрел, там идентично. Что там на входе делается — обычно, считается, на производительность не влияет — посмотрите какой ужас компилируется при векторизации на входе и выходе основного цикла — и всё равно это считается безвредным V>Мелочь — не считается?
Тут скорее таки да.
N>>Но тут проблема не типа как такового — к моменту прихода оптимизаций типы уже стёрты, а диапазона значений. Проверка выбрасывается в правом случае потому, что после конверсии end-beg в беззнаковое оказывается, что в условии немедленного выхода "0 >= (unsigned size_t)(end-beg)" случай "больше" невозможен и его можно не проверять.
V>Именно проблема типа. Более конкретно — знакового или беззнакового типа. Еще более конкретно — проблема выбора этого типа программистом. Оптимизации непричем. Вот именно, что проверка в случае беззнаковых сразу выбрасывается так как она не имеет смысла.
А вы доказали, что она не имеет смысла? Нет, вы просто с помощью правила неявной конверсии форсировали компилятору игнорирование проблемы.
Если бы вы ему просто явно сказали "зуб даю, end >= beg", то вообще могли бы не морочиться заданием знаковости, размера и т.п. — компилятор бы сразу всё сам подобрал. Заодно не было бы эффекта, который коллега Reset показал в соседнем письме, когда он ужал intʼом с 64 до 32 бит переменную цикла.
V> А в случае знаковых — нужна, и она производится, с помощью того самого лишнего теста. Могло бы не быть лишнего теста, если бы использовался "правильный" тип, но программист засунул туда знаковый. Именно по этому я считаю беззнаковый более подходящим для, цитирую ТС: V>
счетчики, индексы и прочие... размеры, количества и прочее...
Да, есть мелкое преимущество в условиях слабой инструментальной базы. Которое компенсируется значительно бо́льшими неприятностями в противоположном случае, если бы неумелое написание цикла загнало переменную в ~0, или end оказался меньше beg.
слава богу, средства позволяют — и вот тут можно было бы и объяснить компилятору, что у нас предусловие — и вот тогда уже никаких проблем против цикла [0..n_elem).
V>Вот она и та самая бритва окама сыграла — если не нужна знаковость — не используй, потому что она не бесплатна, как минимум, этим одним лишним тестом. Вот оно, то самое "не хуже, а иногда чуть-лучше".
Плохая, негодная бритва. См. выше.
N>>Но это корректно только до тех пор, пока реально end >= beg! Вы косвенно, за счёт неявной конверсии к беззнаковому в этом сравнении, гарантируете это условие (и получите очень больно, если нарушите его). V>Не получу) У меня только "счетчики, индексы, размеры, количества...", у меня только такие ситуации в рассмотрении, в которых end >= beg.
Чем гарантируете-то? И что будет завтра с этой гарантией, когда в коде покопается коллега, который не знает всех этих проблем?
Только не рассказывайте, что вы один на проекте. Проблемы таких одиночек сейчас рассматриваются в десятую очередь.
Здравствуйте, netch80, Вы писали:
N>>>А, на входе... я тело цикла смотрел, там идентично. Что там на входе делается — обычно, считается, на производительность не влияет — посмотрите какой ужас компилируется при векторизации на входе и выходе основного цикла — и всё равно это считается безвредным V>>Мелочь — не считается?
N>Тут скорее таки да.
Кому как. В лабораторной синтетике оно конечно не заметно, все взгляды на тело цикла. А на практике будет какая нибудь вложенная-вложенная-вложенная криптуха или мультимедиа-фильтры или еще чего...
N>>>Но тут проблема не типа как такового — к моменту прихода оптимизаций типы уже стёрты, а диапазона значений. Проверка выбрасывается в правом случае потому, что после конверсии end-beg в беззнаковое оказывается, что в условии немедленного выхода "0 >= (unsigned size_t)(end-beg)" случай "больше" невозможен и его можно не проверять.
V>>Именно проблема типа. Более конкретно — знакового или беззнакового типа. Еще более конкретно — проблема выбора этого типа программистом. Оптимизации непричем. Вот именно, что проверка в случае беззнаковых сразу выбрасывается так как она не имеет смысла.
N>А вы доказали, что она не имеет смысла? Нет, вы просто с помощью правила неявной конверсии форсировали компилятору игнорирование проблемы.
Не доказал, аксиоматически установил. По (неявной) аксиоматике ТС "end — beg >= 0", исходя из фразы "по смыслу должно быть беззнаковое целое". Вот и скармливаю ему беззнаковые, явно говорю ему, "беззнаковые, кольцо", "в операторе сравнения делай расширение до беззнакового, как ты обычно и делаешь".
R>>Не очень понимаю, про какие проверки вы все говорите: godbolt (смотреть на серенькое и желтенькое, т.е. содержимое цикла).
V>Добавь -m32, или замени int на long, чтобы размер индекса совпадал с разрядностью платформы.
Ну, так в том и суть. Меняешь unsigned на unsigned long и код становится как с int. Т.е. на AMD64 почему-то не получается инкрементировать eax, а читать rax (с нулями в старших байтах), поэтому возникает лишняя команда (остальной код в цикле не меняется). А в случае с int сразу используется 64-х битный регистр, потому что переполнение int — UB и это используется для "оптимизации", в результате которой код со счетчиком типа int работает не как 32-х битный.
Никаких "проверок" в коде я не увидел. Увидел работу с unsigned в отдельном регистре и пессимизацию из-за того, что 32 бита приходится превращать в 64 для работы с указателем.
Здравствуйте, Reset, Вы писали:
R>>>Не очень понимаю, про какие проверки вы все говорите: godbolt (смотреть на серенькое и желтенькое, т.е. содержимое цикла).
V>>Добавь -m32, или замени int на long, чтобы размер индекса совпадал с разрядностью платформы.
R>Ну, так в том и суть. Меняешь unsigned на unsigned long и код становится как с int. Т.е. на AMD64 почему-то не получается инкрементировать eax, а читать rax (с нулями в старших байтах), поэтому возникает лишняя команда (остальной код в цикле не меняется). А в случае с int сразу используется 64-х битный регистр, потому что переполнение int — UB и это используется для "оптимизации", в результате которой код со счетчиком типа int работает не как 32-х битный.
R>Никаких "проверок" в коде я не увидел. Увидел работу с unsigned в отдельном регистре и пессимизацию из-за того, что 32 бита приходится превращать в 64 для работы с указателем.
Здравствуйте, vopl, Вы писали:
V>Ну, хз. То ли получаются те самые фломастеры, которые разные на вкус и цвет, то ли теперь надо определиться, что за "числа, которые нужны" и почему они без мнимой части, ато вдруг программист будет не только вычитание делать, но еще и корни начнет извлекать..
Тут есть тонкость. Целые вложены в вещественные (в том смысле, что если результат какой-то операции на целых определён, то он такой же и на вещественных) вещественные -- в комплексные. Целые, кстати, ещё вложены и в гаусовы, которые тоже вложены в комплексные. Но гаусовы числа у программистов почему-то не особо в чести
В области тех моделей чисел, что доступны в С, всё не так хорошо, потому, что они вложениями не являются, хотя в чём-то всё равны близки к идеалу.
А вот модули по вычету они вложением в целые, вещественные или комплексные не являются. Они даже не являются вложениями друг в друга.
То есть, например, корректно вычисленная сумма int32_t будет та же и для int64_t. А вот с uint32_t и uint64_t такая фигня уже не проходит
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
V>На мой взгляд в конструкции "Надо нумеровать члены последовательности" + "целое число из диапазона [INT_MIN, INT_MAX]" + "кольцо модулей по вычетам какой-то неизвестной степени двойки" лишнее-то, как раз кольцо...
V>так надо было думать если закольцованность не при чем?
Тип -- это не только значения, но и операции же. Скажем для всех чисел (и тех и других) определены ++ и --.
При этом для беззнаковых глюки (неопределённое поведение или там перескок с одной границы диапазона на другую или насыщене, как ты там доопределить хочешь) будет происходить в дали от реально используемых значений индексов. Если индекс попал с такую область, где можно заметить, что это только отрезок целых, а не все целые, как в python, например, уже сам по себе подозрительный для индекса.
А вот с unsigned это не так.
В любом случае, сейчас int не "зациклен", в отличии от. Так что сейчас выбор ещё более определён...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, netch80, Вы писали:
N>С этим оператором для N-битного числа ты не сможешь сделать, чтобы первая итерация была 2**N-1 (например, в 32-битке нельзя сделать, чтобы первая итерация была для i == 0xFFFF_FFFF): тебе придётся зайти с i == 0, и while() немедленно отвергнет (а выйдя ты как раз 0xFFFF_FFFF и найдёшь в i, но будет поздно). N>А иногда бывает и такая потребность, приходится учитывать. Так что нет тут полного счастья без постпроверки.
Не, ну это совсем особый случай. С этой стороны, кстати, int не шибко лучше.
В любом случае, я отвечал не за спор вообще, а на конкретное замечание, что такую итерацию трудно организовать
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Евгений Музыченко, Вы писали:
E>>2) С-шные unsigned типы -- это не целые. Это кольцо по модулю вычетов. ЕМ>Это важно исключительно там, где не исключено переполнение. Если переполнение исключено, нет никакого смысла говорить о кольце.
1) Тем не менее, кольцо по модулю вычетов одной из степеней 2 -- это общепринятая и закреплённая в стандартах обоих языков семантика unsigned типов в С. То, что ты выдумал себе какую-то иную семантику, восприятие других людей не меняет. Даже то, что Степанов одно время придерживался похожих идей, так и не поменяло семантику этих типов в языке.
2) Формально, переполнения в unsigned не бывает. Но я так понимаю, ты имеешь в виду ситуацию, когда результат операции отличен от результата операции над математическими целыми? Если таки да, то любая разность unsigned чисел легко может дать такое "переполнение"...
E>>Почему тебе кажется более естественным нумеровать элементы последовательности элементами такого кольца, а не ограниченными по модулю целыми числами, для меня загадка... ЕМ>Возможно, потому, что последовательности традиционно нумеруются с единицы или нуля...
И что? Почему тебе кажется, что разница между позицией первого и второго элемента -- это не -1, а 0xFFFFFFFFFFFFFFFF?
Вот другой пример. Массив std::vector<int> v можно рассматривать, как отображение отрезка size_t от 0 до int_достаточная_размерность(v.size()) — 1 (Заметь, даже тут корректно фиг запишешь без апелляции к беззнаковым) на целые числа.
Ну, например, это может быть число смертей/рождений/заболеваний короновирусом и т. д, по дням/неделям/месяцам/годам...
Если это отображение аппроксимируется достаточно гладкой функцией, можно пытаться искать в этом массиве методом Ньютона. То есть мысленно вычитаем из этой функции значение, которое ищем, мы берём две точки, строим через них прямую, смотрим где прямая пересекается с 0, делим расстояние от текущей позиции до той точки в какой-то пропорции и делаем шаг в ту строну и на столько, либо упираемся в границу массива. В этой деятельности мы часто можем рассматривать точки за границами последовательности вполне естественно. В этом смысл отличия целых от кольца, а не в том, что и там и там есть нужное подмножество элементов...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Можно, почему нельзя. Но это именно под последовательность, с относительным доступом. Для такого доступа совершенно естественны числа со знаком, здесь я против них ничего не имею.
То есть тебе кажется удобным для нумерации элементов последовательности и подпоследовательности использовать разные типы? Это странно и, как минимум, противоречит идее Степанова отделить алгоритмы от данных, на которой построен STL...
Кроме того есть и просто последовательности, которые могут быть продолжены в обе стороны. Арифметическая прогрессия, например
Другой пример с поиском по методу Ньютона, я предложил тебе в другой ветке.
Вообще, давай я отвечу на главное сообщение, что бы в одной подветке обсуждать?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>PTRDIFF_MIN -- вообще странно использовать как просто число. Это же специальный тип, для специальных целей...
Откровенно говоря в C++ слишком много типов для всего подряд в предположении что, что-то может быть и не так. Но нет кода не требующего изменений пережившего бы серьезную смену разрядности или архитектуру. Это все "бычий бред".
И это твое выражение — яркий тому пример. Любой двоичный тип данных может быть выражен как просто число. Адрес — это просто число. Это мантра. Повторяй до просветления... При чем за счет ограничения по разрядам — они естественным образом являются и знаковыми и беззнаковыми. Зависит от точки отсчета / аля смысла в них приданного разработчиком. Если же ptrdiff это намек на сегментацию — то C++ с ними не умел и не умеет работать. Да и если вычесть указатели в разных сегментах = безтолковость.
На практике это выливается в то, что (не лично к тебе) вы страдаете какой-то неведомой херней, тема тому доказательство, объясняя какие-то невообразимые вещи. При этом, упорно игнорируя тот факт, что и беззнаковые и знаковые на большинстве операций (т.е. +/-) не отличимы в заданной разрядности регистров. Я это уже выше сказал... просто там дописал позже. В чем спор то?
Тип данных должен ограничивать (или позволять/определять) возможные значения. Вместо этого мы получаем псевдооптимизации за счет андеф. Это херня полная. Если компиляторы так умны — то вместо int/uint подойдет number из JS. Отличный тип — он и int32 и double, а там думайте дальше сами.
Это я все к чему вел. Ненавижу людей, которые начинают рассказывать про странно использовать как число. Адрес — это число. Для тех кто не понял — могу лично повторить. Он измеряется в известных единицах. Межсегментный диф возможен, но он логически в общем случае не будет иметь никакого смысла. А в плоской модели — он будет равен точно заданным координатам солнца. А в реалиях — разнице вычтаемых адресрв в числовом виде.
Итого: выбросьте свою чушь из головы свою C++-ную про херотипы (желательно вместе с вездесущим size_t). Абстракции даются, не для того, что бы абстрагироваться от реального мира, они даются наоборот, подогнать реальный мир, под убогую модель. Вы же, упорно ставите все с ног на голову.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Этого как раз узнавать не нужно. Нужно узнать, для чего математика продолжает пользоваться понятием натурального числа после создания алгебраических структур вроде колец. Вы где-нибудь видели математическую запись суммы или произведения по i/k, где было бы оговорено, что i/k принадлежат кольцу целых чисел?
Я на одной из работ регулярно оперировал суммами и интегралами по разнообразным фигурам в многомерных пространствах, даже
ЕМ>Лично у меня статистика весьма показательная — касты используются исключительно там, где передавать натуральные числа под видом целых вынуждают сторонние API.
Натуральные числа -- это и есть целы. Они в целые вложены. То есть, все натуральные являются целыми, во-первых, и все операции над натуральными, результат которых определён, дают тот же результат с целыми (это если про математику говорить)
Мало того, целые и натуральные равномощны. С кольцом по модулю вычетов всё совсем не так.
И операции дают разные результаты (если, например, кольцо по модулю вычетов 65536, то 1-2 в целых даст -1, в натуральных не определено, а в кольце даст 65535. Натуральное число пять пятёрок (55555), умноженное на два даст натуральное 111110, а для элементов кольца -- 45574 и т. д.)
Кроме того, такое кольцо конечно, а натуральные числа, так же как и целые и рациональные -- счётные.
То есть если рассуждать о вычислениях в программе, как об операциях над математическими объектами, а не над их моделями, представленными в языке, то элементы кольца по модулю вычетов просто не про то, про что индексы.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Mystic Artifact, Вы писали:
MA> Откровенно говоря в C++ слишком много типов для всего подряд в предположении что, что-то может быть и не так. Но нет кода не требующего изменений пережившего бы серьезную смену разрядности или архитектуру. Это все "бычий бред". MA> И это твое выражение — яркий тому пример.
Суть типов вроде ptrdiff_t не в том, что это какие-то супер-совместимые числа, а в читабельности. Так я поясняю читателю кода, из каких соображений выбрал тут разрядность и знаковость целого...
Соответственно, если я использую такой тип для функции вроде fseek, это будет, в первую очередь, непонятно, так как разрядности длины сегмента/адреса и длины файла никак не связаны...
MA>Любой двоичный тип данных может быть выражен как просто число.
Это не совсем правда, или я не понял о чём ты. Например, не любой указатель в С++ может быть так выражен. Указатель на поле, например, -- это двоичный тип?
MA>Адрес — это просто число. Это мантра. Повторяй до просветления...
Это тоже приувелечение. Я много работал на машинах с сегментной организацией памяти. Кроме того, прямо сейчас есть реализации С, которые байт-код интерпретируют и могут проводить любые проверки указателей.
MA>При чем за счет ограничения по разрядам — они естественным образом являются и знаковыми и беззнаковыми. Зависит от точки отсчета / аля смысла в них приданного разработчиком. Если же ptrdiff это намек на сегментацию — то C++ с ними не умел и не умеет работать. Да и если вычесть указатели в разных сегментах = безтолковость.
Формально вычитать нельзя ничего, кроме указателей на один и тот же объект/массив или сразу за его конец.
MA> На практике это выливается в то, что (не лично к тебе) вы страдаете какой-то неведомой херней, тема тому доказательство, объясняя какие-то невообразимые вещи. При этом, упорно игнорируя тот факт, что и беззнаковые и знаковые на большинстве операций (т.е. +/-) не отличимы в заданной разрядности регистров. Я это уже выше сказал... просто там дописал позже.
Это не совсем правда. При вычитаниях unsigned от signed очень даже отличаются, хотя и имеют одинаковое бинарное представление...
MA>В чем спор то?
Я так понял ТС, речь идёт о читабельности кода.
MA> Тип данных должен ограничивать (или позволять/определять) возможные значения. Вместо этого мы получаем псевдооптимизации за счет андеф. Это херня полная. Если компиляторы так умны — то вместо int/uint подойдет number из JS. Отличный тип — он и int32 и double, а там думайте дальше сами.
1) не подойдёт. В С жёсткая статическая типизация
2) кроме множества значений, тип задаёт и набор операций. Проблема unsigned в том, что в области вычитаний они не соответствуют целым ;(
MA> Итого: выбросьте свою чушь из головы свою C++-ную про херотипы (желательно вместе с вездесущим size_t). Абстракции даются, не для того, что бы абстрагироваться от реального мира, они даются наоборот, подогнать реальный мир, под убогую модель. Вы же, упорно ставите все с ног на голову.
Ничего я не ставлю. Просто код, который оперирует с абстракциями так, что нужно всё время помнить о том, как это будет выглядеть в бинарном представлении, обычно читабельным не считают
А вообще да, гражданские -- дураки. Строем не ходят, в машкодах не программируют и всё такое
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Откуда такое пристрастие, кроме как от лени? Вроде как сколько-нибудь массовых процессоров, где беззнаковые целые поддерживались бы ограниченно, не существует. Есть в этом хоть какое-то рациональное зерно?
Насколько я понял, речь идёт о читабельности кода, в первую очередь, а не о тонкостях оптимизации там или ещё чем-то таком.
В целом тут уже была пара флеймов на тему signed vs unsigned, сопоставимых с Linux vs Windows и "плюсы и минусы STL". Так что там можно почитать в количестве
Если же коротко повторить, что бы не обсуждать в нескольких подветках, то
unsigned -- это кольцо по модулю вычетов известных степеней 2
signed -- это отрезок целых с доопределением операции деления на случай некратных чисел при помощи целочисленного деления и взятия остатка.
Соответственно, если мы где-то можем рассуждать о коде, игнорируя разрядность целых, то есть как о числах в математическом смысле, то этот код более читабельный и высокоуровневый, чем такой, где мы не можем игнорировать разрядность переменных.
И в этом смысле семантика индексов -- это тоже отрезок целого ряда, просто другой. И создавать его поверх отрезка целых [INT_MIN, INT_MAX] намного естественнее, чем над кольцом по модулей вычетов.
И, к концу тезисов, небольшой бонус.
Возможно это тебе понравится больше, чем unsigned
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Суть типов вроде ptrdiff_t не в том, что это какие-то супер-совместимые числа, а в читабельности. И поясняю читателю кода, из каких соображений я выбрал тут разрядность и знаковость целого...
Абсолютно согласен и думаю это справедливо.
E>Соответственно, если я использую такой тип для функции вроде fseek, это будет, в первую очередь непонятно, так как разрядности длины сегмента/адреса и длины файла никак не связаны...
Отличный пример. Да!
MA>>Любой двоичный тип данных может быть выражен как просто число. E>Это не совсем правда, или я не понял о чём ты. Например, не любой указатель в С++ может быть так выражен. Указатель на поле, например, -- это двоичный тип?
Единственная причина, почему указатель не мог быть приравнен к соответствующей размерности числу — это разного рода виды теневой памяти и прочее. Сюда же относится и сегментная память, с которой работают текущие процессоры. Но, она всегда настраивается на "плоский" режим, а C++ не позволяет работать с сегментами. Таким образом это все быстро вырождается в то, что мы имеем 32/64-битные адреса и они де числа.
MA>>Адрес — это просто число. Это мантра. Повторяй до просветления... E>Это тоже приувелечение. Я много работал на машинах с сегментной организацией памяти. Кроме того, прямо сейчас есть реализации С, которые байт-код интерпретируют и могут проводить любые проверки указателей.
Я отвечаю по мере. Ну вот ты в теме. Я не отрицаю вообще-то, что, что-то там. Я говорю, что язык не дает. Он дает указатель и все. А из чего он состоит — не дано. Более того, он ссылается всегда сегмент данных. И да. Именно поэтому нельзяпутать из с указателями на функцию, и интерпретировать так же — но готов ли к этому современный код? Нормальный современный код не парится.
E>Формально вычитать нельзя ничего, кроме указателей на один и тот же объект/массив или сразу за его конец.
Правильно. Это как-то контролируется? Есть много нельзя, мало как можно.
E>Это не совсем правда. При вычитаниях unsigned от signed очень даже отличаются, хотя и имеют одинаковое бинарное представление...
Именно. Это и говорит о том, что они не различимы. Разница, в x86 потом происходит только во флагах и их интерпретации. Дело в том, что при 16-битной сетке нет разницы — то ли ты вычьтешь единицу, то ли прибавишь 65535. Мы не говорим о пространственных теориях приводящих к UB. В процессора нет UB, он работает по модулю и отлично и выдает кучу флагов...
MA>>В чем спор то? E>Я так понял ТС, речь идёт о читабельности кода.
Да. Я тут в теме кидал, никто не оценил, почти единственный обратный цикл... компактен и роли не роялит. Ну в моем восприятии — это все крайне вторично. Гораздо сложнее понять что делает код вообще, потом как он делает и ессно низкоуровневые нюансы не рассматриваются, они просто теряются, да и не могу их назвать чем-то странным.
E>1) не подойдёт. В С жёсткая статическая типизация E>2) кроме множества значений, тип задаёт и набор операций. Проблема unsigned в том, что в области вычитаний они не соответствуют целым ;(
Ну я же утрировал... Хотя с JS и number — оно реально представлено реальными int32 и double если нужно.
E>Ничего я не ставлю. Просто код, который оперирует с абстракциями так, что нужно всё время помнить о том, как это будет выглядеть в бинарном представлении, обычно читабельным не считают
Выше было в скобочках, но тут явно — это крик души, а не выпад лично к тебе. Люди которые приблизительно понимают как это будет выглядеть — обречены этой херней страдать. Но иногда — выхлоп того стоит. Спору нет. Это же кстати касается далеко не только C++.
E>А вообще да, гражданские -- дураки. Строем не ходят, в машкодах не программируют и всё такое
Здравствуйте, Mystic Artifact, Вы писали:
MA>>>Любой двоичный тип данных может быть выражен как просто число. E>>Это не совсем правда, или я не понял о чём ты. Например, не любой указатель в С++ может быть так выражен. Указатель на поле, например, -- это двоичный тип? MA> Единственная причина, почему указатель не мог быть приравнен к соответствующей размерности числу — это разного рода виды теневой памяти и прочее. Сюда же относится и сегментная память, с которой работают текущие процессоры. Но, она всегда настраивается на "плоский" режим, а C++ не позволяет работать с сегментами. Таким образом это все быстро вырождается в то, что мы имеем 32/64-битные адреса и они де числа.
1) Почему это С++ не умеет работать с сегментной памятью?
Или имеется в виду, что нет средств разбора сегментного указателя? В принципе на конкретной платформе можно интерпретировать указатель, как структуру с числовыми bitfields и там уже работать, так же как и мз double, например, можно явно мантису выкачать.
2) Указатели в С++ и С не обязательно просто адреса.
Подумай, например, об указателе на метод класса с виртуальной базой
MA> Я отвечаю по мере. Ну вот ты в теме. Я не отрицаю вообще-то, что, что-то там. Я говорю, что язык не дает. Он дает указатель и все. А из чего он состоит — не дано. Более того, он ссылается всегда сегмент данных. И да. Именно поэтому нельзя путать их с указателями на функцию, и интерпретировать так же — но готов ли к этому современный код? Нормальный современный код не парится.
Указатель на функцию может иметь sizeof больше, чем у void*, если что. Например на RISC архитектурах, где популярно иметь регистр с указателем на быструю память, обычно содержит в указателе на функцию адрес кода и адрес нужного сегмента данных.
E>>Формально вычитать нельзя ничего, кроме указателей на один и тот же объект/массив или сразу за его конец. MA> Правильно. Это как-то контролируется? Есть много нельзя, мало как можно.
Ну так UB жеж
MA> Именно. Это и говорит о том, что они не различимы. Разница, в x86 потом происходит только во флагах и их интерпретации. Дело в том, что при 16-битной сетке нет разницы — то ли ты вычьтешь единицу, то ли прибавишь 65535. Мы не говорим о пространственных теориях приводящих к UB. В процессора нет UB, он работает по модулю и отлично и выдает кучу флагов...
Мы же говорим о ПО -- трансляторе и о человеке -- читателе программы. И тому и другому есть разница.
MA> Да. Я тут в теме кидал, никто не оценил, почти единственный обратный цикл... компактен и роли не роялит. Ну в моем восприятии — это все крайне вторично. Гораздо сложнее понять что делает код вообще, потом как он делает и ессно низкоуровневые нюансы не рассматриваются, они просто теряются, да и не могу их назвать чем-то странным.
Можно пропустить нюанс и неверно понять всё в целом. Но я согласен. Борьба со всякими стандартными трюками вроде i-->0 или *dst++ = *src++ довольно бессмысленна. Реальные проблемы разработки лежат в иных плоскостях
E>>Ничего я не ставлю. Просто код, который оперирует с абстракциями так, что нужно всё время помнить о том, как это будет выглядеть в бинарном представлении, обычно читабельным не считают MA> Выше было в скобочках, но тут явно — это крик души, а не выпад лично к тебе. Люди которые приблизительно понимают как это будет выглядеть — обречены этой херней страдать. Но иногда — выхлоп того стоит. Спору нет. Это же кстати касается далеко не только C++.
+100500
E>>А вообще да, гражданские -- дураки. Строем не ходят, в машкодах не программируют и всё такое MA>
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
PD>>… если в результате арифметики с положительными числами результат оказался отрицательным там, где такого не должно быть,то ошибку надо искать в алгоритме …
P>ЕМНИП, это UB, нет? Как можно тут рассуждать должно/не должно?
Это не UB. Имелось в виду, что для знаковых чисел алгоритм дает отрицательное значение там, где его в принципе не должно быть, вследствие ошибки в нем. Например, для вычисления ширины из левой границы вычли правую вместо того, чтобы сделать наоборот.
Здравствуйте, Erop, Вы писали:
E>1) Тем не менее, кольцо по модулю вычетов одной из степеней 2 -- это общепринятая и закреплённая в стандартах обоих языков семантика unsigned типов в С.
Коли уж Вам настолько нравится эта формулировка, что Вы который раз вставляете ее и к месту, и не к месту — расскажите, какой математической абстракцией полностью описывается множество двоичных чисел разрядности N в дополнительном со знаком, и как эта абстракция помогает в программировании численных методов.
E>То, что ты выдумал себе какую-то иную семантику, восприятие других людей не меняет.
Я как раз никакой новой семантики не выдумывал, а использую обычную математическую. Если в математической задаче рассматривается N-мерный вектор (x1; x2; ...; xn), и размерность эта конечна, то никого не заботит, принадлежат индексы элементов конечному или бесконечному множеству натуральных чисел, и как ведет себя множество таких чисел за пределами N.
E>Даже то, что Степанов одно время придерживался похожих идей, так и не поменяло семантику этих типов в языке.
А от того, что многие авторитеты от программирования придерживались и продолжают придерживаться мнения об убожестве Basic в любых его проявлениях, оный Basic хоть немного потерял в популярности?
E>2) Формально, переполнения в unsigned не бывает.
Это верно лишь в том случае, если unsigned соответствующей разрядности используется для математических расчетов внутри того самого кольца. В общей массе таких случаев крайне мало.
E>Но я так понимаю, ты имеешь в виду ситуацию, когда результат операции отличен от результата операции над математическими целыми?
Разумеется.
E>Если таки да, то любая разность unsigned чисел легко может дать такое "переполнение"...
Опять-таки разумеется, если тупо использовать unsigned для решения произвольных математических задач. Только чем для этих произвольных задач будет лучше знаковая арифметика?
E>Почему тебе кажется, что разница между позицией первого и второго элемента -- это не -1, а 0xFFFFFFFFFFFFFFFF?
Я не понимаю, что такое "разница между позицией первого и второго элемента"? Дайте, пожалуйста, определение.
E>Если это отображение аппроксимируется достаточно гладкой функцией, можно пытаться искать в этом массиве методом Ньютона.
Как только мы переходим хоть к методу Ньютона, хоть к любому другому математическому методу, где для удобства используется нулевое значение в знаковой системе координат, мы перестаем оперировать с натуральными числами. В этом месте они должны быть преобразованы в знаковые целые. По-моему, это очевидно, и не требует отдельного разъяснения.
Здравствуйте, Erop, Вы писали:
E>То есть тебе кажется удобным для нумерации элементов последовательности и подпоследовательности использовать разные типы?
Мне кажется удобным использовать натуральные числа для нумерации, и целые — для адресации.
E>Это странно и, как минимум, противоречит идее Степанова отделить алгоритмы от данных, на которой построен STL...
Для универсальных алгоритмов и контейнеров широкого назначения знаковая адресация вполне допустима. А вот для чего, например, argc в main имеет знаковый тип? В каких случаях он может становиться отрицательным?