Вы, кажется, понимаете преимущества использования типа с плавающей запятой. Я стараюсь работать с десятичными числами во всех случаях и полагаюсь на профилировщик, который сообщит мне, вызывают ли операции с десятичными числами узкие места или замедления. В таких случаях я буду «понижать» до double или float, но делаю это только внутренне, и осторожно пытаюсь управлять потерей точности, ограничивая количество значащих цифр в выполняемой математической операции.
В общем, если ваше значение временное (не используется повторно), вы можете безопасно использовать тип с плавающей запятой. Настоящая проблема с типами с плавающей запятой заключается в следующих трех сценариях.
- Вы агрегируете значения с плавающей запятой (в этом случае ошибки точности складываются)
- Вы строите значения на основе значения с плавающей запятой (например, в рекурсивном алгоритме)
- Вы занимаетесь математикой с очень большим количеством значащих цифр (например,
123456789.1 * .000000000000000987654321
)
ИЗМЕНИТЬ
Согласно справочной документации по десятичным дробям C #:
Ключевое слово decimal обозначает 128-битный тип данных. По сравнению с типами с плавающей запятой десятичный тип имеет большую точность и меньший диапазон, что делает его пригодным для финансовых и денежных расчетов.
Итак, чтобы прояснить мое приведенное выше утверждение:
Я предпочитаю проектировать десятичные дроби во всех случаях и полагаюсь на профилировщик, который сообщит мне, вызывают ли операции с десятичными числами узкие места или замедления.
Я когда-либо работал только в тех отраслях, где используются десятичные дроби. Если вы работаете над физическими или графическими движками, вероятно, гораздо выгоднее разрабатывать для типа с плавающей запятой (float или double).
Decimal не является бесконечно точным (невозможно представить бесконечную точность для нецелого в примитивном типе данных), но он намного точнее, чем 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
Как видите, даже несмотря на то, что мы добавляем из той же исходной константы, результаты двойного действия менее точны (хотя, вероятно, будут округлены правильно), а число с плавающей точкой гораздо менее точным, до такой степени, что оно было уменьшено до две значащие цифры.
person
Michael Meadows
schedule
29.04.2009