← Все вопросы

Что такое decimal в C# и чем отличается от double?

Задан 10 дней назад406 просмотров2 ответа
2

Считаю сумму заказа, и при сложении цен типа 0.1 + 0.2 вылезает 0.30000000000000004. Использую double. Коллега сказал «для денег бери decimal». Чем decimal реально отличается от double и почему именно для денег? И что за буква m в конце числа?

2 ответа

5
✓ Принятый ответ — помог автору

Ты наткнулся на классику. Дело в том, как число хранится в памяти.

double — это двоичное число с плавающей точкой (IEEE 754). Компьютер хранит его в двоичной системе, а дроби вроде 0.1 в двоичке периодические — как 1/3 в десятичной. Поэтому 0.1 хранится приближённо, и при сложении накапливается ошибка:

double d = 0.1 + 0.2;
Console.WriteLine(d);          // 0.30000000000000004

decimal хранит число в десятичной форме (128 бит) и точно представляет десятичные дроби. Для денег это критично — там каждая копейка должна биться.

decimal price = 0.1m + 0.2m;
Console.WriteLine(price);      // 0.3

Что за m. Это суффикс литерала: он говорит компилятору «это decimal, а не double». Без него 0.1 — это double, и присвоение в decimal без суффикса даст ошибку компиляции. По аналогии: f — float, d — double, m — decimal (m от money, удобно запоминать).

decimal total = 19.99m * 3;    // правильно
// decimal bad = 19.99;        // ошибка: 19.99 это double

Когда что использовать:

  • decimal — деньги, финансы, любые величины, где важна точная десятичная арифметика. Минус: медленнее и диапазон по величине меньше.
  • double — научные/инженерные расчёты, графика, физика, где нужен огромный диапазон и небольшая погрешность не страшна. Он быстрее (его считает FPU процессора).

Правило: считаешь рубли и копейки — decimal. Считаешь траекторию или среднюю температуру — double. И никогда не сравнивай double через == напрямую, сравнивай с допуском (epsilon).

3

Подкреплю практикой: в большинстве ORM и БД денежные поля маппятся именно на decimal (SQL-тип decimal/numeric), так что и в C# держи их decimal по всей цепочке — иначе на стыке получишь скрытые конвертации и потерю точности.

Ещё совет по округлению: для денег используй Math.Round(value, 2, MidpointRounding.AwayFromZero), потому что банковское округление по умолчанию (ToEven) может удивить на значениях вроде 2.5.

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект