Saferoll писал(а):
Вот я сейчас пишу реализацию FOR, считая что тип ББ INTEGER имеет 4 байта, а тип LONGINT 8. И никак иначе не получается, "сферическая абстракция в вакууме" так в "вакууме" и останется, на реальном железе надо что-то поконкретнее.
Н.Вирт в Обероне-07 оставил всего один целочисленный тип INTEGER, что сделало язык малопригодным для работы ИМХО. Но за этим скрывается идея унифицировать взгляд на целые. Т.е. ЦЕЛОЕ, а идеальный компилятор сам уже разберётся, короткое оно или длинное, ибо первое важно для огромных массивов данных, а второе — для точных расчётов с большими числами. Но на практике ещё не встречается компиляторов, которые смогли бы верно распознать длину типа автоматически. Тенденции как раз обратные — всё приводится к одному типу большой разрядности (например, в Python — 64 бита).
Тот железный факт, что в основу любого типа ложится конкретная разрядность, конечно не способствует появлению Оберон-программ, ориентированных на "универсальное целое", и, полагаю, нам при проецировании типов на конкретные разрядности следует придерживаться традиций платформы и здравого смысла.
Кстати, отмечу, что в Си, например, при взгляде на присваивание a = b нельзя заведомо сказать, не зная контекста выполнения и типов переменных, может ли быть здесь потеря разрядности. На Обероне, благодаря его SHORT, — это видно сразу. Притом Вирт, благодаря его концепции "поглощения меньших типов большими, а целых — вещественными", сделал 2 полезных вещи: во-первых, явно видно, где разрядность может быть понижена, а во-вторых не нужно её явно повышать там, где она и так не потеряется. Не нужно явно приводить целые к вещественным (ведь такое приведение осуществляется без потерь). И в то же время, никто не мешает явно повышать разрядность с помощью LONG. Вобщем, придумано явно удачнее, чем в Си.
Когда я активно писал на Си, то пользовался машинным представлением чисел там и сям. Меня не смущали ни заёмы, ни переполнения. Числа — это набор бит, и всё тут. В Си так принятно. Когда же довелось плотно поработать на Обероне — понял всю ценность отношения к числовым типам как к абстрактному представлению чисел. Я понял, что в идеале SHORT не надо использовать для отбрасывания старших битов. Да, ББ и, тем более, Ofront не отловят такое использование SHORT. Но по замыслу SHORT применяется только для того, чтобы сказать: вот обычное целое переходит в короткое целое (или длинное переходит в обычное целое), это волевой акт работы ума программиста, но программист не отбрасывает биты (его на данном этапе даже не интересует как представлено число в памяти машины), он просто явно указывает на понижение мощности типа, что добавляет программе ясности и лёгкости чтения/понимания.
И в идеале конечно если при выполнении SHORT результат усекается, умный рантайм должен обязательно предупредить. Таким образом, программисту становится ценнее каждый бит информации, и его приходится учитывать, а не отбрасывать. Поэтому сейчас я, например, не просто привожу INTEGER к BYTE, а добавляю MOD 256. Код становится немного больше (хотя умный компилер может и такое оптимизировать), но повышается понятность. Снижается зависимость результата вычислений от рамок типа. Так что советую не отсекать старшие биты SHORT'ом, не в этом его соль (Вместе с тем понятно, что при понижении разрядности отрицательных — биты по-любому будут отброшены).
Но это, понятно, в идеале. Что для системного программирования уже не столь существенно, поскольку нужна высокая эффективность.
Saferoll писал(а):
Не видел исходники ETH Oberon и AOS, но может там использование LONGINT оправдано?
Да, конечно оправдано, но, скорее, в силу традиции. ETH Oberon разрабатывался в то время, когда программисты оперировали разрядностью 8-16-32, и удобно было спроецировать систему типов на эти величины. С тех пор как 32-битность стала фактически нормой, востребованность типов 8-16 несколько снизилась, а потребность в 64 выросла. Что было учтено при проектировании BlackBox и, конечно, Ofront. Но Йозеф, сделав некоторые намётки в виде чтения разрядности типов из Ofront.par, всё-таки не довёл эту идею до реализации вполне, да и не особо это было ему нужно, поскольку для его потребностей хватало 32-битного числового типа, и он использовал традиционную в то время систему 8-16-32. Но когда это было? Где-то в 90-х, как показывают даты создания файлов Ofront'а. Это старичок ещё тот.
Хватит ли нам 3 + 1 целочисленных типа или придётся всё равно в итоге добавить-таки HUGEINT? Пока что вроде бы хватает, я как-то скептически отношусь к 128-битной разрядности. Если такие процессоры и станут массовыми, оставшиеся 32 и 64 они целиком не потеснят. Адресовать 64-мя битами уже можно дофига памяти, да и расчёты врятли будут переводиться все поголовно под 128. Так что вот тогда-то нам и понадобится, быть может, HUGEINT. А может быть и нет.
Saferoll писал(а):
Гибкость – это хорошо, но не следует ей злоупотреблять. Ранее я писал, что по стандарту (как я его понимаю) целые типы могут совпадать друг с другом. Но следует ли задавать такое совпадение в конфигурации? Ведь для чего-то программист использует другой тип, а не этот (например для контроля за переполнением, как я L). А если вдруг типы совпадут, то никакой обработки переполнения не произойдет. Получится, что программа работает неверно. Так что, я бы считал, что «SHORTINT строго меньше INTEGER, который, в свою очередь, строго меньше LONGINT».
Да, я тоже не вижу особого смысла делать эти типы одной разрядности. И хотя сейчас в XDev типы LONGINT и INTEGER 32-битны, однако это временная мера, поскольку LONGINT мы подготавливаем к 64-битности.
Saferoll писал(а):
Если хотим ввести 64-разрядность, то да. Правда мне придется опять переделывать реализацию FOR и существенно. Потому что сейчас я обошел переполнение B-A приведением к LONGINT. А если А,В сами будут LONGINT? 128-битной арифметики нет, придется как-то по-другому. Вообще-то придумал уже как, просто сложнее немного.
Saferoll писал(а):
Я делал вычисление количества повторений цикла FOR. Это количество выражается в общем случае беззнаковой константой, в знаковый тип не влезает. Но для тех чисел что не влезают, можно подобрать отрицательное значение, которое из-за переполнения сработает нужным нам образом. Например, в однобайтовом знаковом типе нет значения 130, но можно использовать «-126», вычитая из которого 1 можно дойти до нуля (-127, -128,127,126, …1,0) за 130 шагов, что и требуется.
Saferoll писал(а):
К сожалению, это будет работать только для 1 и 2 байтовых типов. Для 4-байтового возможно переполнение при вычислении количества итераций. Будем думать.
Олег, нам нужна формула!
Возьмём вычисление количества повторений цикла FOR. Если оно может быть свёрнуто в константу (константное выражение), временной мерой может быть использование в этом случае вычислений в рамках типа ББ LONGINT (64 бита). Поскольку пока что в Ofront'е нет поддержки 64 бит, нам хватит диапазона 0..MAX(LONGINT) аж с головой.
Поскольку, опять же, Ofront не поддерживает 64 бита, то можно ограничиться при расчётах типом ББ LONGINT, но результат сохранять (упаковывать SHORT'ом) в тип INTEGER, считая его неотрицательным.
Кстати, только что проверил компилировать в SDCC что-то такое:
Код: "OBERON"
Компилятор показывает warning, но генерирует положенный 0. А на s = 255 даже не ругается, хотя s — тип знаковый.
Когда мы касаемся расчётов количества повторений цикла FOR в нашей программе уже на типах Ofront'а, то здесь есть проблема, как ты верно заметил. Нам нужно записать формулу, которая вычислит а рантайме на типах Ofront'а количество повторений цикла. И вот какой появляется вопрос. Очевидно, что для количества повторов цикла FOR мы используем описанную Ofront'ом для этих целей переменную xxx__for__1, тип которой совпадает с переменной-счётчиком цикла. Понятно, что если интерпретировать эту переменную как беззнаковую, то её диапазона хватит для вмещения количества повторов, что вытекает из того факта, что границы счётчика заданы именно в рамках этого типа. Таким образом, мы лишаемся проблемы сложного вычисления типа этой переменной, перекладывая это на плечи программиста, да так, что он об этом и не узнает.
Когда работает формула вычисления количества повторов — ей не хватает разрядности, и надо прибегнуть к расчётам на более высокой разрядности, как я понял, не так ли? Но тут есть существенная проблема: для максимального типа LONGINT, какой бы разрядности он ни был, уже нету и не может быть более широкого типа с большей разрядностью, так что это тупик. Отсюда появляется 2 решения: либо приводить если не переменную, то хотя бы расчёты к unsigned (средствами Си через макрос, заданный в SYSTEM.h, как ты хотел сделать для возможности реализовать детали цикла FOR на ассемблере), либо же оставаться в рамках знаковых расчётов средствами рантайма для данного типа. Но ни в коем случае не переходить на тип большей разрядности, потому что: a) его точная разрядность нам неизвестна; b) она может оказаться такой же; c) наконец, типа, бОльшего, чем заданный, может не оказаться в нашем ящичке с инструментами. Думаю, этих контраргументов хватит.
Так что нам нужна формула.
Которая отработает в рамках одного заданного типа. И что-то мне подсказывает, что надо начать с рантайма, а для константных свёрток использовать ту же формулу. При этом нас не ограничивают заём/переполнение, мы можем накрайняк вводить новые макроопределения в SYSTEM.h и т.д.