Десятичная или двойная скорость

double decimal finance

23043 просмотра

7 ответа

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

Вся моя математика работает с числами не более 5 знаков после запятой и не больше ~ 100 000. У меня есть ощущение, что все они могут быть представлены как двойные в любом случае без ошибки округления, но никогда не были уверены.

Я бы пошел вперед и сделал бы переход от десятичных чисел к двойным для очевидного преимущества в скорости, за исключением того, что в конце дня я все еще использую метод ToString для передачи цен на биржи, и мне нужно убедиться, что он всегда выводит число I ожидать. (89,99 вместо 89,99000000001)

Вопросов:

  1. Действительно ли преимущество в скорости так велико, как показывают наивные тесты? (~ 100 раз)
  2. Есть ли способ гарантировать вывод из ToString, что я хочу? Гарантируется ли это тем, что мой номер всегда представим?

ОБНОВЛЕНИЕ: мне нужно обработать ~ 10 миллиардов обновлений цены, прежде чем моё приложение сможет работать, и я внедрил с десятичной дробью по очевидным причинам защиты, но для включения требуется ~ 3 часа, удвоение значительно уменьшит время моего включения , Есть ли безопасный способ сделать это с двойниками?

Автор: Superman Источник Размещён: 12.11.2019 09:50

Ответы (7)


79 плюса

Решение
  1. Арифметика с плавающей точкой почти всегда будет значительно быстрее, потому что она поддерживается непосредственно аппаратным обеспечением. До сих пор практически ни одно широко используемое оборудование не поддерживает десятичную арифметику (хотя это меняется, см. Комментарии).
  2. Финансовые приложения должны всегда использовать десятичные числа, количество страшных историй, связанных с использованием плавающей запятой в финансовых приложениях, бесконечно, вы можете найти много таких примеров с помощью поиска Google.
  3. Хотя десятичная арифметика может быть значительно медленнее, чем арифметика с плавающей запятой, если только вы не тратите значительное количество времени на обработку десятичных данных, влияние на вашу программу, вероятно, будет незначительным. Как всегда, сделайте соответствующее профилирование, прежде чем начать беспокоиться о разнице.
Автор: Robert Gamble Размещён: 30.11.2008 11:46

23 плюса

Здесь есть две отдельные проблемы. Во-первых, достаточно ли точности у двойника для хранения всех нужных вам бит, а во-вторых, где он может точно представлять ваши числа.

Что касается точного представления, вы правы, чтобы быть осторожным, потому что точная десятичная дробь, такая как 1/10, не имеет точного двоичного аналога. Однако, если вы знаете, что вам нужно только 5 десятичных цифр точности, вы можете использовать масштабированную арифметику, в которой вы оперируете числами, умноженными на 10 ^ 5. Так, например, если вы хотите точно представить 23.7205, вы представляете его как 2372050.

Давайте посмотрим, достаточно ли точности: двойная точность дает вам 53 бита точности. Это эквивалентно 15+ десятичным цифрам точности. Так что это позволит вам пять цифр после десятичной запятой и 10 цифр перед запятой, что кажется достаточным для вашего приложения.

Я бы поместил этот код C в файл .h:

typedef double scaled_int;

#define SCALE_FACTOR 1.0e5  /* number of digits needed after decimal point */

static inline scaled_int adds(scaled_int x, scaled_int y) { return x + y; }
static inline scaled_int muls(scaled_int x, scaled_int y) { return x * y / SCALE_FACTOR; }

static inline scaled_int scaled_of_int(int x) { return (scaled_int) x * SCALE_FACTOR; }
static inline int intpart_of_scaled(scaled_int x) { return floor(x / SCALE_FACTOR); }
static inline int fraction_of_scaled(scaled_int x) { return x - SCALE_FACTOR * intpart_of_scaled(x); }

void fprint_scaled(FILE *out, scaled_int x) {
  fprintf(out, "%d.%05d", intpart_of_scaled(x), fraction_of_scaled(x));
}

Вероятно, есть несколько грубых пятен, но этого должно быть достаточно, чтобы вы начали.

Никаких накладных расходов на добавление, стоимость умножения или деления удваивается.

Если у вас есть доступ к C99, вы также можете попробовать масштабированную целочисленную арифметику с использованием int64_t64-битного целочисленного типа. Что будет быстрее, будет зависеть от вашей аппаратной платформы.

Автор: Norman Ramsey Размещён: 01.12.2008 01:24

20 плюса

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

Автор: Craig Размещён: 30.11.2008 11:59

12 плюса

  1. Да; Программная арифметика действительно в 100 раз медленнее аппаратной. Или, по крайней мере, это намного медленнее, и коэффициент 100, отдача или взятие порядка, является правильным. В старые добрые времена, когда вы не могли предположить, что каждый 80386 имел сопроцессор с плавающей запятой 80387, у вас также была программная симуляция двоичной плавающей запятой, и это было медленно.
  2. Нет; Вы живете в фантастической стране, если думаете, что чисто двоичная плавающая точка может точно представлять все десятичные числа. Двоичные числа могут объединять половину, четверть, восьмое и т. Д., Но, поскольку для точного десятичного числа 0,01 требуется два коэффициента: одна пятая и один коэффициент - одна четверть (1/100 = (1/4) * (1/5) * (1) / 5)) и поскольку одна пятая не имеет точного представления в двоичном виде, вы не можете точно представить все десятичные значения двоичными значениями (потому что 0,01 является контрпримером, который не может быть представлен точно, но представляет огромный класс десятичных чисел, который не может быть представлен точно).

Итак, вы должны решить, можете ли вы справиться с округлением, прежде чем вызывать ToString (), или вам нужно найти какой-то другой механизм, который будет иметь дело с округлением ваших результатов, когда они преобразуются в строку. Или вы можете продолжать использовать десятичную арифметику, поскольку она останется точной и станет быстрее после выпуска машин, которые поддерживают новую десятичную арифметику IEEE 754 в аппаратном обеспечении.

Обязательная перекрестная ссылка: что должен знать каждый ученый об арифметике с плавающей точкой . Это один из многих возможных URL.

Информация о десятичной арифметике и новом стандарте IEEE 754: 2008 на этом сайте Speleotrove .

Автор: Jonathan Leffler Размещён: 01.12.2008 12:59

8 плюса

Просто используйте длинное и умножьте на степень 10. После того, как вы закончите, разделите на ту же степень 10.

Автор: John Размещён: 03.05.2009 03:20

6 плюса

Десятичные дроби всегда должны использоваться для финансовых расчетов. Размер чисел не важен.

Самый простой способ объяснить это с помощью некоторого кода на C #.

double one = 3.05;
double two = 0.05;

System.Console.WriteLine((one + two) == 3.1);

Этот бит кода выведет False, хотя 3.1 равен 3.1 ...

То же самое ... но с использованием десятичной дроби:

decimal one = 3.05m;
decimal two = 0.05m;

System.Console.WriteLine((one + two) == 3.1m);

Это теперь распечатает True !

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

Автор: mezoid Размещён: 01.12.2008 01:13

1 плюс

Я отсылаю вас к моему ответу на этот вопрос .

Используйте long, сохраняйте наименьшее количество, которое вам нужно отследить, и отображайте значения соответственно.

Автор: gbjbaanb Размещён: 17.12.2008 12:31
Вопросы из категории :
32x32