Арифметическая ошибка с двойным переполнением стека

Я заметил небольшую ошибку в некоторых арифметических вычислениях с использованием double. Это действительно странно, всегда есть небольшая ошибка и / или дополнительная значащая цифра.

Сначала я использую atof для преобразования числа, которое имеет две значащие цифры, которые я читаю из текстового файла (затем я записываю их в вектор):

 // Puts into vector
double ask_file, bid_file; // Values of ask and bid from file
double cur_conversion = 0.16;
ask_file = cur_conversion*atof(values[0].c_str());
bid_file = cur_conversion*atof(values[1].c_str());

Затем я делаю арифметику (из другого класса, два разных объекта):

diff = OKC->bid_val() - BV->ask_val(); // diff
diff2 = OKC->ask_val() - BV->bid_val(); // diff2

Это вывод:

BV Askfile: 245.267 Bidfile: 245.078
OKC Askfile: 248.82 Bidfile: 248.73
diff: 3.4628 diff2: 3.7416

Как видите, ошибка в обоих расчетах. diff = 3.463, а НЕ 3.4628. И diff2 = 3.742, а НЕ 3.7416.

Вы знаете, что происходит ??

0

Решение

Проблема в том, что в общем случае невозможно точно представить дробные десятичные значения, используя двоичные числа с плавающей запятой. Например, 0.1 представляется как 1.000000000000000055511151231257827021181583404541015625E-1 когда используешь double (вы можете использовать это онлайн анализатор определить значения). При вычислении с этими округленными значениями количество необходимых двоичных цифр будет превышать число, которое может быть представлено, и значение будет дополнительно округлено, что приведет к большей ошибке. Конечно, все это кроется в Бумага Гольдберга на что указывает комментарий Эда Хила.

Существует ряд альтернативных представлений, которые вы можете использовать для точного вычисления с десятичными значениями. Если представление не использует представление произвольного размера, оно будет точно только в некотором диапазоне значений. Типичные варианты:

  1. Использование большого целочисленного представления вместе с подходящим десятичным масштабированием.
  2. Строки (или BCD) цифр.
  3. Представление с фиксированной точкой, которое в основном представляет собой целое число вместе с фиксированным десятичным показателем, где показатель неявен в типе с фиксированной точкой (или, например, в качестве аргумента шаблона).
  4. Вместо использования двоичной плавающей запятой вы должны использовать десятичные числа с плавающей запятой. Плавающие точки являются просто представлением знак, мантисса, и показатель степени со значением, вычисляемым как (-1)знак * мантисса * базапоказатель степени. double использует базу 2 но для десятичных вычислений вы бы использовали базу 10,
  5. Используя два больших целых числа, вы можете представить значение как рациональное число.
  6. Есть несколько других вариантов, но приведенный выше список — это то, что я считаю практическими вариантами.

В зависимости от выбора реализации различные операции более или менее просты в реализации, а точные операции различаются. Например, за исключением представления с использованием рациональных операций деления всегда будут округлены, когда делитель не может быть представлен как произведение 2 а также 5,

Какое представление будет работать лучше для вашего приложения, зависит от ваших потребностей. Если у вас есть только торговые цены в диапазонах, типичных для акций, может работать представление с фиксированной точкой. Если вам необходимо охватить все виды значений, с которыми вы можете столкнуться в финансах, например, национальные долги, а также процентные ставки, вам потребуется более 64 бит для представления с фиксированной запятой, и представление с десятичной плавающей запятой может быть лучшим представлением. В зависимости от того, нужно ли вам передавать и / или сохранять значения, представление с фиксированным размером может не потребоваться, и в этом случае другие представления могут быть разумным выбором.

1

Другие решения


По вопросам рекламы [email protected]