Pull to refresh
0
0
Спиридонов Юрий Маркович @Innotor

Изобретатель

Send message
Так у double точность примерно 15 десятичных знаков после запятой, почему вы ждете, что он будет 18 знаков на входе правильно обрабатывать?

Поэтому и ожидаешь, что все 15 цифр обещают быть верными.

К сожалению, если округлять число 1.506242359842874900e-27 вашей программой, то мы получим ошибку в другую сторону. Округление даст 1.50624235984288e-27
Что выдаст вам ваш компилятор, после
double a = atof(«1.50624235984287501e-17»);
У меня Code::Bloks выдал: 1.50624235984287e-17
Моя тестовая программа выдает 1.50624235984288e-17
Где правда?
Попробуйте этот вариант.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
double a = atof(«1.506242359842875009e-27»);
printf("%.18e\n", a);
printf("%.14e\n", a);

return 0;
}
Мне Code::Bloks выдал: 1.50624235984287e-27
Я предложил альтернативный вариант округления десятичных чисел, представленных в двоичном коде. И он работает. Это не значит, что стандартные средства эту задачу не решают. Выбор за программистом.
Но нам же хочется, чтобы все было быстрее и точнее:)
Вы правы, когда речь идет о вычислениях в арифметике одной базы. Когда вычисления ведутся только в десятичной или только двоичной арифметике. Но, когда мы десятичную арифметику реализуем в двоичных кодах, все становится не так очевидно.
Вот вам простой пример: (9876543210988,06-9876543210987,04)*10=102
Эта вычислительная схема устойчива? И знаков после запятой всего 2. Но посчитайте в формате double и получите 102,14844. (Проверено на Excel).

Вы упустили, что пример приведен для float, у которого p=24. Для такого случая в примере все правильно. Вы же использовали double.
И что? Это здесь причем? Мы же не о целых числах говорим, а о числах с плавающей точкой/запятой.
А чем вас округление при выводе-то не устраивает?

Потому, что округлять часто надо в процессе вычислений, которые выполняются над двоичными числами. Иначе десятичные ошибки могут быстро накапливаться.
А каково назначение IEEE754?
И какое упорство достойно лучшего применения, чем стремление сделать вычисления более точными и более быстрыми (читай простыми)? Вы не предложили ни одной работающей программы, решающей проблему правильного округления десятичных чисел в двоичном коде. Не могут предложить простого решения и разработчики стандарта. Хотя ломают над этим голову.
См., например, 1. Modern Computer Arithmetic Richard P. Brent and Paul Zimmermann 2. Correctly Rounded Floating-point Binary-to-Decimal and Decimal-to-Binary Conversion Routines in Standard ML
3. Hardest-to-Round Cases – Part 2 и другие. Все эти ребята входят в рабочую группу по ревизии стандарта IEEE754 и пока проблему не решили.
И потом, стандарт должен удовлетворять потребности всех пользователей решающих разнообразные задачи, а не только тех с которыми вы сталкиваетесь. Уж извините. Накипело. Поэтому, давайте по делу.

Правда, это ничего не изменит, потому что р в программе не используется.

Согласен, что касается p, вы правы.
Ну так это же не я сетовал на то, что в бинарном представлении как-то маловато разрядов. Это были вы.

В каком месте нашего диалога я жаловался на малость разрядов в бинарном представлении?
Про тяжелость вы сами придумали или есть какие-то данные с полей?

Может я ошибаюсь, но длинная арифметика, в том числе для чисел с плавающей точкой реализована в десятичном формате с использованием BCD. А этот формат требует существенных программно-аппаратных затрат. Именно поэтому в железе он до сих пор мало где реализован.
pow и frexp, которые не факт что легенькие.

Если отвлечься от программной реализации этих функций, а посмотреть чисто на алгоритм, то frexp легко реализуется аппаратно, т.к. надо просто считать поле двоичной экспоненты, а pow легко реализуется в табличном виде.
Т.е. он не нужен.
А зачем тогда запрашивать его у пользователя?

Программа тестовая. Она тестирует округление для любого формата в котором p<=53. Вы можете задать p=24 для формата single, или любое другое значение p.
Библиотеки дл работы с числами любой длины существуют уже много-много лет.

Эти программы и библиотеки существуют, но они очень тяжелые и потому используются в крайних случаях. Зачем покупать дорогой автомобиль, если до места — рукой подать?
а данные снабжены информацией о числе верных знаков

Где кроется эта информация о десятичных верных знаках в двоичном числе формата double?

От того, что вы умножите результат на миллиарды, вы получите ответ с погрешностью, увеличенной в миллиарды раз

О чем и речь. А если бы перед вычитанием числа были правильно округлены, мы, в нашем примере, получили бы 0. И какое бы число на 0 ни умножай, в результате будет 0.
Для случая, когда надо округлить число до определенного знака, в библиотеке <math.h> отдельной функции нет


Потому и нет, что нет удовлетворительных алгоритмов.
потому обычно используют умножение числа и последующее после округления деление,

Это работает для случая нулевой экспоненты (без сдвига). Если экспонента двоичного числа существенно превышает количество разрядов двоичной мантиссы, или она отрицательная, определить значение коэффициента 10^E, на который надо разделить число, а потом его умножить, сложная задача. См. 1,2,3.
Программа написана для проверки работы алгоритма с переменными типа double. Для этого формата p=53, так что неявно это число присутствует. Любой формат двоичного числа с p<=53, гарантированно даст правильный результат округления до N<=15 значащих десятичных цифр. Если принять p>=1000, то в результате нормализации двоичная мантисса все равно будет округлена до 53 цифр. Поэтому, поскольку программа написана для формата double, гарантировать правильного округления для p>53 мы не можем. Надо перейти к другому, более длинному формату. Но, насколько я знаю, операционных регистров с количеством разрядов p близким к 1000 пока не существует.
x= 0.011 — это число, которое вы хотите округлить до N=2
X= 0.0109999999999999994 — это десятичное представление двоичного числа, которое получилось после конвертации десятичного x в формат double.
Xr= 11e-3=0.011 — число, полученное округлением до N=2 числа x с целочисленной десятичной мантиссой.
xr=0.0109999999999999994 — двоичное представление числа Xr в формате double

Да, и еще. Округление ведется не до N знаков после точки/запятой, а до N значащих десятичных цифр.
Статья про алгоритм, а не про программу. Эта простенькая программа призвана только продемонстрировать правильность алгоритма. Она самодельная, т.к. я не профессиональный программист. Если есть ошибки в алгоритме, давайте обсудим.
Посчитайте в Excel следующее выражение:
1,12345678987658000000E56 + 4,12345678987654000000E56--7,92721801979018000000E+42 = -5,2469135797532E+56.
Вас ответ устраивает?
Если это окончательный результат, то на погрешность можно закрыть глаза, а если результат умножить на E+56, то ответ будет мало походить на ноль.
Я думаю, что в случае, когда в алгоритме присутствует вычитание, желательно аргументы перед вычитанием округлить в десятичном эквиваленте. К чему может привести катастрофическая отмена хорошо описано в многочисленных источниках.
Точность p, это десятичное число p<=53.

Information

Rating
Does not participate
Location
Минск, Минская обл., Беларусь
Registered
Activity