Когда я должен использовать double вместо decimal?



Я могу назвать три преимущества использования double (или float) вместо decimal:




  1. использует меньше памяти.

  2. быстрее, потому что математические операции с плавающей запятой изначально поддерживаются процессорами.

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


но эти преимущества, по-видимому, применимы только к вычислительным интенсивным операциям, таким как те, которые найдены в программном обеспечении моделирования. Конечно, двойники не должны использоваться, когда точность требуется, например, финансовые расчеты. Так есть ли какие-либо практические причины, чтобы когда-либо выбрать double (или float) вместо decimal в "нормальных" приложений?



изменить добавить:
Спасибо за все замечательные ответы, я узнал от них.



еще один вопрос: несколько человек отметили, что двойники могут более точно представлять реальные числа. Когда объявлено, я бы подумал, что они обычно более точно представляют их. Но верно ли утверждение, что точность может снизиться (иногда значительно) при выполнении операций с плавающей запятой?

1146   12  

12 ответов:

Я думаю, что вы суммировали преимущества довольно хорошо. Однако вы упускаете один момент. Элемент decimal тип только более точен при представлении базовый 10 числа (например, используемые в валютных/финансовых расчетах). В общем, то double тип будет предлагать по крайней мере такую же большую точность (кто-то поправит меня, если я ошибаюсь) и определенно большую скорость для произвольных вещественных чисел. Простой вывод: при рассмотрении использования, всегда используйте double Если вам нужен base 10 точность decimal предложения.

Edit:

Что касается вашего дополнительного вопроса о снижении точности чисел с плавающей запятой после операций, это немного более тонкий вопрос. Действительно, точность (я использую термин взаимозаменяемо для точности здесь) будет неуклонно уменьшаться после выполнения каждой операции. Это происходит по двум причинам:

  1. тот факт, что некоторые числа (наиболее очевидно десятичные) не могут быть действительно представлены в форме с плавающей запятой
  2. ошибки округления, как если бы вы делали расчеты вручную. Это во многом зависит от контекста (сколько операций вы выполняете), являются ли эти ошибки достаточно значительными, чтобы оправдать много размышлений.

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

для более детального обзора частных случаев, когда могут быть введены ошибки в точности, см. раздел точность статья в Википедии. Наконец, если вы хотите серьезно углубленное (и математическое) обсуждение чисел с плавающей запятой/операций на машинном уровне, попробуйте прочитать часто цитируемую статью Что Каждый Компьютер Ученый Должен Знать Об Арифметике С Плавающей Запятой.

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

В общем случае, если ваше значение является переходным (не используется повторно), вы можете безопасно использовать тип с плавающей запятой. Реальная проблема с типами с плавающей запятой заключается в следующих трех сценариях.

  1. вы агрегируете значения с плавающей запятой (в этом случае соединение ошибок точности)
  2. вы строите значения на основе значения с плавающей запятой (например, в рекурсивном алгоритме)
  3. Вы делаете математику с очень большим числом значащих цифр (например, 123456789.1 * .000000000000000987654321)

EDIT

По словам справочная документация по десятичным знакам C#:

The decimal ключевое слово обозначает a 128-битный тип данных. По сравнению с типы с плавающей запятой, десятичный тип имеет большую точность и меньшую ряд, который делает его соответствующим для финансовые и денежные расчеты.

Итак, чтобы прояснить мое выше утверждение:

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

Я никогда не работал в отрасли, где число благоприятных. Если вы работаете над phsyics или графическими движками, вероятно, гораздо выгоднее разрабатывать для типа с плавающей запятой (float или double).

десятичное число не является бесконечно точным (невозможно представить бесконечную точность для нецелого в примитивном типе данных), но это гораздо точнее, чем double:

  • decimal = 28-29 значащих цифр
  • double = 15-16 значащих цифр
  • float = 7 значащих цифр

EDIT 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

используйте decimal для базовых 10 значений, например, финансовых расчетов, как предлагали другие.

но double обычно более точен для произвольных вычисленных значений.

в следующем примере doubleResult ближе к 1, чем decimalResult:

// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;

Итак, снова возьмем пример a портфолио:

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

  • вес каждой строки в портфеле (= рыночная стоимость / сумма (рыночная стоимость)) обычно лучше представить как двойной.

используйте двойник или поплавок, когда вам не нужна точность, например, в платформерной игре, которую я написал, я использовал поплавок для хранения скоростей игрока. Очевидно, мне не нужна супер точность здесь, потому что я в конце концов в int для рисования на экране.

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

расчет 1/6 из $ 100 дает $16.66666666666666..., таким образом, значение, выполненное в рабочем листе, будет $16.666667. Как двойной, так и десятичный должны дать этот результат точно до 6 знаков после запятой. Однако мы можем избежать любой накопительной ошибки, перенеся результат вперед как целое число 16666667. Каждый последующий расчет может быть выполнен с той же точностью и перенесен аналогично. Продолжая пример, я вычисляю Техасский налог с продаж на эту сумму (16666667 * .0825 = 1375000). Добавление двух (это короткий рабочий лист) 1666667 + 1375000 = 18041667. Перемещение десятичной точки обратно дает нам 18.041667, или $18.04.

в то время как этот короткий пример не будет дайте кумулятивную ошибку, используя double или decimal, довольно легко показать случаи, когда просто вычисление double или decimal и перенос вперед накапливали бы значительную ошибку. Если правила, по которым вы работаете, требуют ограниченного числа десятичных знаков, сохранение каждого значения в виде целого числа путем умножения на 10^(требуемое число десятичных знаков), а затем деления на 10^(требуемое число десятичных знаков), чтобы получить фактическое значение, позволит избежать кумулятивной ошибки.

в ситуациях, когда доли копеек не встречаются (например, в торговом автомате), нет никаких оснований использовать нецелые типы вообще. Просто думайте об этом, как о подсчете пенни, а не долларов. Я видел код, где каждый расчет включал только целые копейки, но использование двойного привело к ошибкам! Целочисленная только математика удалила проблему. Поэтому мой нетрадиционный ответ, когда это возможно, отказаться от двойных и десятичных.

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

Примечание: это сообщение основано на информации о возможностях десятичного типа от http://csharpindepth.com/Articles/General/Decimal.aspx и моя собственная интерпретация того, что это значит. Я предположу, что Double-это нормальная двойная точность IEEE.

примечание 2: самый маленький и самый большой в этом посте reffer к величине числа.

плюсы "десятичная".

  • "decimal" может представлять точно числа, которые могут быть записаны как (достаточно короткие) десятичные дроби, двойные не могут. Это важно в финансовых бухгалтерских книгах и тому подобное, где важно, чтобы результаты точно соответствовали тому, что человек делает расчеты дали бы.
  • "decimal "имеет гораздо большую мантиссу, чем"double". Это означает, что для значений в пределах его нормализованного диапазона "decimal" будет иметь гораздо более высокую точность, чем double.

минусы десятичных

  • это будет намного медленнее (у меня нет тестов, но я может быть, больше), decimal не выиграет от какого-либо аппаратного ускорения, а арифметика на нем потребует относительно дорогого умножения/деления на степени 10 (что намного дороже, чем умножение и деление на степени 2), чтобы соответствовать показателю перед сложением/вычитанием и вернуть показатель в диапазон после умножения/деления.
  • decimal переполнится раньше, чем удвоится. десятичное число может представлять только числа до ±296-1 . Для сравнения double может представлять числа до почти ±21024
  • decimal будет underflow раньше. Наименьшие числа, представимые в десятичном виде, равны ±10-28 . Для сравнения double может представлять значения до 2-149 (приблизительно 10-45) если поддерживаются субномальные числа и 2-126 (приблизительно 10-38), если они не.
  • decimal занимает в два раза больше памяти, чем в два раза.

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

используйте плавающие точки, если вы оцениваете производительность по правильности.

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

ваше приложение нуждается в быстром расчете или у него будет все время в мире, чтобы дать вам ответ? Это действительно зависит от типа приложения.

графический голоден? поплавок или двойной достаточно. Анализ финансовых данных, метеоритный удар a планетарный вид точности ? Для этого потребуется немного точности :)

Decimal имеет более широкие байты, double изначально поддерживается процессором. Десятичное основание-10, поэтому десятичных двойное преобразование происходит во время компьютерной десятичное число.

For accounting - decimal
For finance - double
For heavy computation - double

имейте в виду, .NET CLR поддерживает только математику.Пау (двойной,двойной). Десятичное число не поддерживается.

.NET Framework 4

[SecuritySafeCritical]
public static extern double Pow(double x, double y);

двойные значения будут сериализованы в научную нотацию по умолчанию, если эта нотация короче десятичного отображения. (например .00000003 будет 3e-8) десятичные значения никогда не будут сериализованы в научную нотацию. При сериализации для потребления внешней стороной это может быть рассмотрено.

зависит от того, для чего вам это нужно.

потому что float и double-это двоичные типы данных, которые у вас есть некоторые трудности и ошибки в пути в круглых числах, поэтому, например, double будет округлять 0.1 до 0.100000001490116, double также будет округлять 1/3 до 0.33333334326441. Проще говоря, не все вещественные числа имеют точное представление в двойных типов

к счастью, C# также поддерживает так называемую десятичную арифметику с плавающей запятой, где числа по десятичной системе счисления, а не двоичной системе. Таким образом, десятичная точка с плавающей запятой-арифметика не теряет точность при хранении и обработке чисел с плавающей точкой. Это делает его очень подходящим для расчетов, где требуется высокий уровень точности.

Comments

    Ничего не найдено.