Decimal vs Double в C#: Битва за точность (и ваши нервы)
Вы когда-нибудь писали калькулятор зарплаты или, скажем, симулятор полета мяча, и вдруг получали 0.30000000000000004 вместо ожидаемых 0.3? Если да — добро пожаловать в клуб. Вопрос "decimal vs double! - Which one should I use and when?" — один из самых популярных на Stack Overflow, и не зря. Кажется, что это просто выбор между двумя типами чисел с плавающей точкой. Но на самом деле это выбор между финансовой катастрофой и научной точностью (или наоборот).
В этой статье мы разберем этот вечный спор раз и навсегда. Вы узнаете, чем тип decimal в C# отличается от типа double, где какой применять и, самое главное, — как не облажаться в 2025 году. Поехали!
Коротко о главном: что скрывается под капотом?
Прежде чем мы начнем кидаться аргументами, давайте заглянем в спецификацию. У каждого типа своя архитектура, и именно она определяет, где он будет королем, а где — изгоем.
Double (он же System.Double)
- Размер: 64 бита (8 байт).
- Точность: Примерно 15-17 значащих цифр.
- Диапазон: Огромный (от ±5.0 × 10−324 до ±1.7 × 10308).
- Представление: С плавающей точкой по стандарту IEEE 754 (двоичное).
- Производительность: Высокая. Процессоры оптимизированы для работы с double.
Decimal (он же System.Decimal)
- Размер: 128 бит (16 байт).
- Точность: 28-29 значащих цифр.
- Диапазон: Меньше (от ±1.0 × 10−28 до ±7.9 × 1028).
- Представление: Десятичное (по сути, целое число со сдвигом запятой).
- Производительность: Ниже. Нет аппаратной поддержки на CPU, все вычисления — программные.
Главное отличие, которое нужно запомнить: double — это двоичная дробь, а decimal — десятичная. Именно из-за этого возникают знаменитые "ошибки округления".
Почему double "ломает" деньги, но идеален для науки?
Представьте, что вы пытаетесь записать число 0.1 в двоичной системе. Это будет бесконечная периодическая дробь: 0.0001100110011.... Компьютер обрезает её, и получается маленькая, но критичная погрешность. Для научных расчетов (симуляции погоды, физика, графика) эта погрешность накапливается, но в пределах допустимой нормы. Для финансов — это "украденная" копейка у миллиона пользователей.
Decimal же хранит число как целое (например, 1/10 = 0.1 хранится как целое число 1 и информация о том, что запятую нужно сдвинуть на один знак). Поэтому точность decimal идеальна для десятичных дробей.
Когда использовать decimal в C#? (Спойлер: для денег)
Золотое правило, которое спасет ваш проект: используйте decimal для финансовых, денежных и бухгалтерских расчетов. Если в вашей переменной лежат рубли, доллары, проценты по кредиту или НДС — ваш выбор однозначен.
Вот пример, почему double — зло для кошелька:
using System;
public class MoneyExample
{
public static void Main()
{
// Плохой пример с double
double price = 10.99;
double tax = 0.07;
double totalDouble = price + (price * tax);
Console.WriteLine($"Double total: {totalDouble}"); // Может выдать 11.759300000000001
// Хороший пример с decimal
decimal priceDec = 10.99m;
decimal taxDec = 0.07m;
decimal totalDec = priceDec + (priceDec * taxDec);
Console.WriteLine($"Decimal total: {totalDec}"); // Точно: 11.7593
}
}
Видите эту "лишнюю единицу" в конце у double? В масштабах банковской транзакции это приведет к ошибке сверки баланса. Тип decimal в C# гарантирует, что 0.1 + 0.2 будет строго равно 0.3.
Когда decimal — не лучший выбор:
- Производительность критична. Если вам нужно обсчитать миллиард значений в секунду (например, в играх или машинном обучении), decimal будет тормозить в 10-20 раз медленнее double.
- Большие числа. Диапазон decimal ограничен. Для астрономических расчетов (расстояние до звезд) double незаменим.
Когда использовать double в C#? (Наука, графика и скорость)
Double — это "рабочая лошадка" для математики, физики, 3D-графики и статистики. Он быстрый, легкий и имеет огромный диапазон. Если вам не нужна абсолютная точность до 28 знака после запятой — смело берите double.
Пример: рассчет траектории снаряда.
using System;
public class PhysicsExample
{
public static void Main()
{
// Идеально для double
double velocity = 150.5; // м/с
double angle = 45.0; // градусы
double g = 9.81; // ускорение свободного падения
double radians = angle * Math.PI / 180.0;
double maxHeight = (velocity * velocity * Math.Sin(radians) * Math.Sin(radians)) / (2 * g);
Console.WriteLine($"Max height: {maxHeight} meters");
// Здесь decimal не нужен, так как погрешность в 0.0001 мм нас не волнует.
}
}
Тип double в C# отлично подходит для:
- Научных и инженерных расчетов.
- Обработки изображений и звука.
- Игровой физики (Vector3, матрицы трансформаций).
- Хранения больших массивов данных (экономия памяти).
Лучшие практики для C# в 2025 году: чек-лист
Чтобы вы больше никогда не гадали, запомните эту шпаргалку. Это лучшие практики C# 2025, основанные на реальном опыте разработчиков со Stack Overflow.
1. Финансы и валюта → Всегда Decimal
Никаких исключений. Цены, налоги, зарплаты, скидки — только decimal. Используйте суффикс m (money) для литералов: decimal salary =