| Погрешности long double (25.06.2026). |
|
| 2026 - Июнь | |
| 25.06.2026 09:12 | |
Любое дело, к которому прикасается государство РФ и его прихвостни, - быстро превращается в говно. Начиная от высшего образования и заканчивая ценой-качеством утильсборного автопрома, блокировками интернета, испоганиванием яндекса-мейла-вконтакте-макса, торговой биржей, стоматологией, экстремизмом и т.д. - опыта на этом сайте более чем хватает. Если хочется жить нормально - рано или поздно приходит понимание: не стоит связываться с такого рода вещами (однозначно, проиграешь). Напротив, дела о внутренностях техники-физики-химии, подчиняющиеся вполне конкретным законам, - вполне и изучаемы, и доводимые до конца (если продукция сделана качественно). А главное, при должной сноровке, - полностью предсказуемы, т.к. государству (пока) до этого дела нет (исключения - Astra Linux, ГИС и т.д.). Предсказуема ли погрешность long double? Это смотря под каким соусом её жрать.
Началось всё с написания самой тяжёлой и масштабной в жизни работы: анализатора площадных зон планеты. Еле-еле хватило точности long double в Qt v.5.3 64bit на Astra Linux v.1.4: при разрешении мировой карты 1мс (100мс/с: требовалась точность много больше общепринятой) - погрешность работы с уравнениями прямых составила < 0.5мс (прям пограничное значение - когда ещё работают математические правила округления). Данный результат не только был везением: он не был достойно протестирован - оставаясь слабым местом в программе (избирательное ручное тестирование), наравне с тестированием площадных зон (использовались те, которые давали, а не созданные по своим правилам). Время шло - вернулся к задаче создания генератора площадных зон. И если ранее это была стажёрская задачка с уравнениями прямых без каких-либо ограничений, необходимости точных вычислений и на малом участке мира (он, естественно, не справился) - то теперь это превратилось в масштабную задачу, охватывающую всю планету. Понимая, что точность требуется просто бешеная: было принято решение начать с удобства разработки - Astra и Qt получили пинок под зад. Вернувшись в старую-добрую хрюшу и ворча на Borland C++ Builder v.6.0, приходит и эксперимент: потянут ли 32-битные программы такую задачу. А это разбивается на подэксперименты: хватит ли разрешения TBitmap для самоконтроля генерации зон с аппроксимацией (43200x32400 пикселей), хватит ли погрешности long double для получения результирующей погрешности <0.5мс и т.д. Изучение теории с помощью разных ИИ - оказалось ещё одним гвоздём в их гроб (результаты были получены именно для данной версии среды программирования): - предлагает повысить точность вычислений long double с помощью _control87(), PC_80, MCW_PC в float.h. А константы PC_80 и не существует. В 2 соседних предложениях заявляет: PC_80 - не константа, но чуть ниже - пишет её постоянное значение. Естественно, использование значений 0x2 и 0x30000 (последние пробовал разные) - вообще точность вычислений уронило ниже плинтуса; - предлагает для сравнения long double функцию IsEqual с использованием fabs() и константы LDBL_EPSILON (нельзя long double сравнивать с помощью ==). Ладно fabs() медленная, но до 15 знаков всего - она только для double подходит, а для double в уравнениях прямых был уже произведён тест: провал - не хватает точности. ИИ тупо анализирует интернет, видит кучу одинаковых функций в ответах на каких-нибудь форумах - и просто помещает её в ответ без какого-либо анализа правильности; - то же самое касается функции randomize(). Она завязана на секундомере ОС: если её располагать в цикле >1Гц - рандомайзер сходит с ума и становится константогенератором. Правильное её расположение - однократно в FormCreate()/FormShow(), но так как весь интернет увешан неправильным её использованием - ИИ и считает неправильное правильным. В очередной раз поняв, что от ИИ толку не добьёшься, - пришлось сформулировать свои критерии расчёта погрешности: - LDBL_EPSILON имеет значение 1.084202172485504434e-019L. Но это не имеет никакого значения; - чем большее число помещается в переменные с плавающей точкой - тем больший хвост после запятой будет отсечён. На этом основан баг pow() в разных средах программирования на основе C: пока свою на основе __int64 не напишешь - будет врать (в Qt с 53 степени, в другом каком-то языке с 22 - что намекает на разные погрешности одних и тех же типов данных в разных средах разработки); - в уравнении Y = kX + b: k - выступает в роли множителя погрешности, находящейся в X. Получается, если X и k большие - погрешность при использовании такого уравнения будет бешеная; - к счастью, макетирование показало: приравнивание двух long double друг другу - дополнительную погрешность не порождает (число со своей погрешностью - точная копия). Реализация: - написана своя функция получения погрешностей при работе с long double без fabs(); - использовалось уравнение прямой Y = kX + b и реверсивные вычисления (самоконтроль ПО и обнаружение погрешности в одном флаконе); - использован генератор координат с максимумом 1млн; - реверсивные вычисления невозможны с параллельными прямыми относительно друг друга и относительно осей координат (деление на 0) - в этом случае, к одной из координат прибавлялась 1мс; - выполнены 126млрд вычислений дважды. Показали невыход за погрешность 4.5475e-013L - между 12-13 знаками, отличный результат. Но стали возникать сомнения - было решено перебором всех возможных комбинаций точек найти наибольшую погрешность; - как и в прошлый раз, выгодно использовать уравнение прямой с 2 неизвестными, а не с 3: планируется огромное количество вычислений и потребление ~1ГБ RAM на каждую площадную зону. Вот тут-то писец и пришёл: - при разрешении карты 129.6млн миллисекунд - число вычислений для всех уравнений прямой составит 129.6млн4, а для пересечения всех прямых - 129.6млн8. С квадриллионными числами уже работал без проблем - но это число выглядит как выставленный штраф гуглу от РФ (государственные гопари создавали контент с нарушениями - для создания огромного количества заявок - для выписывания гигантского штрафа невыполнения заявок - чтобы вытеснить гугл из РФ - чтобы все в православном хуяндексе принудительно сидели - благо, не получилось). Тут многопоточность уже ничего не значит: вычисления займут тысячи лет; - было решено пока с разными дискретами в циклах 500.000-800.000мс пробежаться - и была получена погрешность 4.621e-04L. При этом, она сосредоточена в X1 2млн, Y1 ~115.8млн, X2 23.1млн и каком-то Y2. Скорее всего, там просто гигантское число k, - анализ проводиться не будет; - чтобы корректно уточнить полученную погрешность и не погрязнуть в вычислениях, нужно дискреты циклов обозначить 1 градусом или 1 минутой. Это охватит 360 или 21600 возможных вариантов наклона прямой - и при этом многомиллионные значения попадут и в X, и в Y (тут главное пограничный результат захватить, используя граничное число 129600001). Результат градусных наклонов - 4.6362...e-04L за несколько часов, минутных - будет работать в 12.96млн раз дольше. Нужно решение о многопоточности принять, или системник оставить на неделю-2 включённым, или всё вместе сделать, или забить болт. А также исходники причесать; - получается, максимум числа был увеличен с 1млн до 129.6млн (+2 десятичных разряда), а погрешность - улетела в скратосферу +9 разрядов. Промежуточные итоги для работы с уравнениями прямых на карте мира с разрешением 1мс при 100мс/с: - погрешность вычислений составит <4.6363e-04L миллисекунд при использовании самого точного типа данных long double - меньше никак не сделать. Но для определения точки пересечения двух прямых - эта погрешность будет больше; - есть вероятность, что погрешность будет меньше, чем в Qt. А это будет значить, что в принудительно навязанной Qt: удалось реализовать то чудище несколько лет назад успешно - чисто по везению, пройдясь по лезвию ножа; - теперь, зная реальное значение погрешности, - можно функцию её определения переписать многопоточно (есть за какое число цепляться для оценки качества работы потоков). Плюс получение результата одним 6-часовым прогоном - наверняка, мало; - поэтому, продолжение (или болт) - следует... (добавлено 26.06.2026) Причёсывание исходников привело к тому, что возросла скорость расчёта погрешности с 6ч до 4ч (номинал погрешности - тот же). Перерыв весь Math.h, нашёл внутри fabsl() - скорость работы как у моей самописной, работает корректно. Создание механизма многопоточных вычислений - теперь скорее рутина, чем интересное программирование (и перетаскивание заготовок из одного проекта в другой - происходило медленно и с постоянным зеванием). Однако заготовка из грязновой стала чистовой - и теперь просто подключается к любому проекту парой файлов, а не как ранее была сильно завязана на форме. Когда она заработает - есть смысл её потом добавить в полезные исходники и в обучающий материал с многопоточной программой расчёта простых чисел. |
|
| Обновлено ( 26.06.2026 22:35 ) |