From 977853885c7f28cf020a39e6d72ac52db0b036bb Mon Sep 17 00:00:00 2001 From: Marat Zimnurov Date: Sun, 4 Oct 2026 20:35:05 +0000 Subject: [PATCH] docs(tasks): 5245 asks for a remainder over the exact integer The exact integer got plus, minus, times and the five order words in task 1908; the remainder and the division were left as a named refusal. The owner asked the question that makes the gap concrete: a genetic algorithm example needs a random number generator, and every linear congruential generator ends its step with "mod". So the refusal now has a case against it, and the case is written down. WHAT ACTUALLY LIES, named precisely, because the obvious version of this argument is false. Reproduced on the binary of task 1908 against Python's own exact integers: Lehmer, seed 2000000000, multiplier 16807 0 apart glibc, seed 2000000000, mult 1103515245 + 12345 45 apart glibc, seed 1234567891, same mult and increment 40 apart The product ALONE is exact here: 2000000000 * 1103515245 is above 2^53, but it needs only 51 significant bits and a double holds it digit for digit. The INCREMENT is what breaks it - it clears the low zeros and the number then needs more than 53 bits. On the full seed range it is worse: 2147483647 * 1103515245 + 12345 needs 60 significant bits. Hence the corner the author is in: the glibc multiplier pushes the product past 2^53 from seed 8162279 up, and only 0.38 percent of the range modulo 2^31 is still guaranteed exact. Lehmer's 16807 is exact everywhere - the largest product over the whole range, 2147483646 * 16807, is 36092757638322 and below 2^53. So today the choice is Lehmer or silently wrong numbers. THE COST SECTION CORRECTS MY OWN EARLIER CLAIM. I said in the report closing 1908 that division's termination rests on a measure of its own. It does not: lines 608-699 of bignum.flang carry no "decreases" at all. "Bity razryada" counts a non-negative counter down and the two step functions fold over a fixed list of 22 bits. The explicit measure, "Zapas algoritma Evklida", belongs to the GCD at lines 701-780, not to the division. So the cost here is volume - 7 functions and 86 non-empty lines against 1 function and 23 for subtraction with borrow - and not a termination proof. The task says so, and says which of my own words were wrong, so the next reader does not inherit the mix-up. Three words from 1908 are reused rather than rewritten: "Umnozhit razryady na maloe", "Raznost razryadov" and "Menshe razryadov" are exactly what the bit step stands on. One trap is named with them: "Raznost razryadov" below zero returns wrapped digits, not a refusal - the refusal lives one level up in "Tochnoe vychitanie" - so guarding with "Menshe razryadov" before the subtraction is the caller's duty. The number of new functions is NOT measured and says so, because it depends on a question nobody has answered yet: whether a fast path for a one-digit divisor is wanted. Measured, not predicted: run-script tasks:check code 0 (203 tasks, 189 free, 8 in work, 6 done), task-numbers-guard code 0 (203 open, 401 taken by closed ones, 0 duplicates), prose-numbers-guard 224 of 224 marks agree both with this file present and with it removed, so no counted mark moves. --- ...der-and-division-over-the-exact-integer.md | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 docs/tasks/5245-remainder-and-division-over-the-exact-integer.md 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`, до +двоичного доезжает только перепечаткой семени.