Вы, кажется, замечательно получаете преимущества использования типа с плавающей запятой. Я склонен проектировать десятичные дроби во всех случаях и полагаюсь на профилировщик, чтобы сообщить мне, если операции с десятичной дробью вызывают узкие места или замедления. В этих случаях я буду «понижать» до удвоения или смещения, но делаю это только внутренне, и тщательно пытаюсь управлять потерей точности, ограничивая количество значащих цифр в выполняемой математической операции.
В общем, если ваше значение является временным (не используется повторно), вы можете безопасно использовать тип с плавающей запятой. Настоящая проблема с типами с плавающей запятой заключается в следующих трех сценариях.
- Вы агрегируете значения с плавающей запятой (в этом случае ошибки точности составят)
- Вы строите значения на основе значения с плавающей точкой (например, в рекурсивном алгоритме)
- Вы делаете математику с очень большим количеством значащих цифр (например,
123456789.1 * .000000000000000987654321
)
РЕДАКТИРОВАТЬ
Согласно справочной документации по десятичным знакам C # :
Десятичное ключевое слово обозначает тип данных 128-битным. По сравнению с типами с плавающей запятой десятичный тип имеет большую точность и меньший диапазон, что делает его пригодным для финансовых и денежных расчетов.
Итак, чтобы уточнить мое вышеупомянутое утверждение:
Я склонен проектировать десятичные дроби во всех случаях и полагаюсь на профилировщик, чтобы сообщить мне, если операции с десятичной дробью вызывают узкие места или замедления.
Я только когда-либо работал в отраслях, где десятичные дроби являются благоприятными. Если вы работаете над физическими или графическими движками, вероятно, гораздо выгоднее проектировать для типа с плавающей запятой (с плавающей или двойной).
Десятичное число не является бесконечно точным (невозможно представить бесконечную точность для нецелого в примитивном типе данных), но оно гораздо точнее, чем double:
- десятичная = 28-29 значащих цифр
- double = 15-16 значащих цифр
- float = 7 значащих цифр
РЕДАКТИРОВАТЬ 2
В ответ на комментарий Конрада Рудольфа , пункт № 1 (выше), безусловно, является правильным. Агрегация неточности действительно усугубляет. Посмотрите код ниже для примера:
private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;
public static void Main(string[] args)
{
Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
float asSingle = 0f;
double asDouble = 0d;
decimal asDecimal = 0M;
for (int i = 0; i < ONE_MILLION; i++)
{
asSingle += THREE_FIFTHS;
asDouble += THREE_FIFTHS;
asDecimal += (decimal) THREE_FIFTHS;
}
Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
Console.ReadLine();
}
Это выводит следующее:
Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000
Как вы можете видеть, даже если мы добавляем из одной и той же исходной константы, результаты удвоения менее точны (хотя, вероятно, будут округлены правильно), а число с плавающей точкой гораздо менее точно, до точки, где оно было уменьшено до две значащие цифры.