diff --git a/docs/tasks/5245-remainder-and-division-over-the-exact-integer.md b/docs/tasks/5245-remainder-and-division-over-the-exact-integer.md new file mode 100644 index 000000000..c34c3e50f --- /dev/null +++ b/docs/tasks/5245-remainder-and-division-over-the-exact-integer.md @@ -0,0 +1,132 @@ +--- +номер: 5245 +заголовок: Над точным целым нет остатка и деления, а без остатка не пишется конгруэнтный генератор +статус: свободна +приоритет: P2 +исполнитель: — +ветка: — +команда: первая +карта: Чего в языке нет вовсе +рядом: 1412, 1413, 1908 +нужность: без остатка точное целое не закрывает тот самый случай, ради которого заведено — счёт за 2⁵³ с переносом результата обратно в узкий ряд +--- + +# 5245. Над точным целым нет остатка и деления + +## Шаги воспроизведения + +1. Остаток над точным целым: + +```flang +модуль «A remainder over the exact integer is refused» + +тотальная функция «Остаток» + принимает а: точное целое, б: точное целое + возвращает точное целое + а остаток от б +``` + +2. Деление — та же программа с `а делить на б`. +3. Команда: `bootstrap/flang check <файл>`. + +## Что происходит + +``` +$ bootstrap/flang check <остаток> +FLANG_TYPE … строка 6, столбец 5: над точным целым «mod» не определено: +точное целое знает «плюс», «минус» и «умножить на» +<файл>: не проверено — замечаний 1 код 1 +$ bootstrap/flang check <деление> +FLANG_TYPE … строка 6, столбец 5: над точным целым «div» не определено: +точное целое знает «плюс», «минус» и «умножить на» +<файл>: не проверено — замечаний 1 код 1 +``` + +Отказ сознательный, не недосмотр: задача 1908 ввела над точным целым `плюс`, +`минус`, `умножить на` и пять слов порядка, а деление с остатком оставила +названным отказом. Довод был в объёме: разрядное деление в +`flang/stdlib/bignum.flang` — **7 функций и 86 непустых строк** (строки +608–699) против **1 функции и 23 непустых строк** у вычитания с займом +(строки 222–245). + +## Что должно быть + +Остаток и деление над точным целым определены, и слово отказа уходит. + +**Случай, который без остатка не пишется — линейный конгруэнтный генератор.** +Его шаг — `(зерно · множитель + добавка) остаток от модуль`, и без остатка +последнего действия взять негде. Померено на двоичном задачи 1908 против +точных целых Python: + +| шаг | `число` (flang) | точно | расхождение | +|---|---|---|---| +| Лемер, зерно 2 000 000 000, множитель 16807 | 33614000000000 | 33614000000000 | 0 | +| glibc, зерно 2 000 000 000, множитель 1103515245, добавка 12345 | 2207030490000012300 | 2207030490000012345 | 45 | +| glibc, зерно 1 234 567 891, тот же множитель и добавка | 1362364488706010600 | 1362364488706010640 | 40 | + +Что именно врёт, названо точно, чтобы довод не рассыпался при проверке: +**произведение само по себе здесь точно** — `2 000 000 000 · 1103515245` +даёт 2 207 030 490 000 000 000, это выше 2⁵³, но значащих бит в нём 51, и +double держит его знак в знак. Ломает **добавка**: она сносит нижние нули, и +числу нужно уже больше 53 бит. На полном ряду зёрен хуже: +`2147483647 · 1103515245 + 12345` требует **60 значащих бит**. + +Отсюда порог, который и загоняет автора в угол: множитель glibc выводит +произведение за 2⁵³ при зерне **8 162 279** и выше. Дальше точность не +гарантирована — какое зерно уцелеет, зависит от того, сколько у произведения +нижних нулей, и полагаться на это нельзя. Ряд, на котором точность +гарантирована, — **0,38 %** от ряда по модулю 2³¹. Множитель Лемера 16807 +точен всюду: наибольшее произведение по всему ряду, `2147483646 · 16807`, +равно 36 092 757 638 322 и ниже 2⁵³. Поэтому сегодня у автора генератора +выбора нет — либо Лемер, либо числа, неверные молча. + +## Обходной путь + +Множитель, укладывающийся в 2⁵³ вместе с добавкой и модулем, — так и сделано +в примере генетического алгоритма (`flang/experimental/`): взят Лемер +`16807 mod 2147483647`. Обход закрывает только этот множитель; `число` остаётся +IEEE-754, и законы кольца над ним ядро не доказывает. + +## Цена, насколько она померена + +- **Объём** померен: 7 функций и 86 непустых строк в `bignum.flang`. +- **Своя мера завершения НЕ нужна**, и это проверено чтением, а не памятью: + в строках 608–699 нет ни одного `убывает`. `«Биты разряда»` убывает по + счётчику `осталось: неотрицательное`, а `«Бит в деление»` и + `«Разряд в деление»` рекурсии не несут вовсе — второе сворачивает список + ровно из 22 бит. Явная мера `«Запас алгоритма Евклида»` относится к `НОД` + (строки 701–780), а не к делению; не перепутать. +- **Что уже готово и переиспользуется**: `«Умножить разряды на малое»`, + `«Разность разрядов»` и `«Меньше разрядов»` из `flang/self/interpret.flang` + задачи 1908 — ровно те три слова, на которых стоит `«Бит в деление»`. + Осторожно с одним: `«Разность разрядов»` ниже нуля даёт завёрнутые разряды, + а не отказ (отказ живёт выше, в `«Точное вычитание»`), поэтому стеречь + `«Меньше разрядов»` перед вычитанием — обязанность вызывающего. +- **Число новых функций НЕ мерено**: оценку по образцу `bignum.flang` в 7 + функций брать нельзя, пока не решено, нужен ли быстрый путь для делителя в + один разряд. + +## Чего эта задача НЕ закрывает + +`равен` и `не равен` над точным целым сравнивают структурно и приведения не +несут — так решено в 1908 намеренно, потому что при вычислении тип неизвестен, +а `список числа` и `точное целое` — одно значение. Остаётся один путь к +неканоническому значению: `список числа`, собранный списочными словами, +поданный в позицию точного целого и сравнённый `равен` до любого действия над +ним. Закрывается сужением втекания `список числа` в `точное целое` — это +задача 1412, не эта. + +## Когда задача сделана + +Программа из шагов воспроизведения проходит `check` с кодом 0. Полный шаг +генератора на точном целом даёт тот же ответ, что точные целые Python, на всех +трёх строках таблицы выше, включая `2147483647 · 1103515245 + 12345` с его 60 +значащими битами. Есть проба с числами за 2⁵³ и подделкой, на которой ответ +обязан измениться. Деление на ноль отвергнуто названным словом. + +## Где живёт правка + +`flang/self/interpret.flang` (разрядное деление и остаток), +`flang/self/types.flang` (снять отказ `mod` и `div` над точным целым), +`flang/proof/probes/exact-integer/**` (пробы). Всё под `flang/self`, до +двоичного доезжает только перепечаткой семени.