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

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

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

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

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

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

Ответ 1

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

Edit:

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

Для более подробного обзора конкретных случаев, когда ошибки в точности можно ввести, см. раздел "Точность" статьи What Every Компьютерный ученый должен знать о арифметике с плавающей точкой.

Ответ 2

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

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

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

РЕДАКТИРОВАТЬ

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

Ключевое слово decimal обозначает 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

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

Ответ 3

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

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

Например, если вы хотите рассчитать вес каждой строки в портфеле, используйте double, потому что результат будет почти до 100%.

В следующем примере 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;

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

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

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

Ответ 4

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

Ответ 5

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

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

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

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

Ответ 6

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

Ответ 7

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

Примечание2: наименьшее и наибольшее в этом сообщении ссылается на величину числа.

Плюсы "десятичного".

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

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

  • Это будет намного медленнее (у меня нет тестов, но я бы предположил, что, по крайней мере, на порядок больше, может быть, больше), десятичный выигрыш не будет выгоден от какого-либо аппаратного ускорения, и арифметика на нем потребует относительно дорогого умножения/деления на полномочия из 10 (что намного дороже, чем умножение и деление на степени 2), чтобы соответствовать экспоненте перед сложениями/вычитанием и возвращать экспоненту обратно в диапазон после умножения/деления.
  • decimal будет переполняться раньше, чем double. decimal может представлять только числа до & plusmn; 2 96 -1. При сравнении double может представлять числа до почти & plusmn; 2 1024
  • decimal будет раньше использоваться. Наименьшие числа, представимые в десятичных числах, являются & plusmn; 10 -28. При сравнении double может представлять значения до 2 -149 (приблизительно 10 -45), если поднромальные числа поддерживаются и 2 -126 (приблизительно 10 -38), если они не являются.
  • decimal занимает вдвое больше памяти, чем double.

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

Ответ 8

Точность - основное различие, когда double - это двойной тип данных с плавающей запятой с двойной точностью (64 бит), а десятичный - это тип данных с плавающей запятой в 128 бит.

Двойной - 64 бит (15-16 цифр)

Десятичный - 128 бит (28-29 значащих цифр)

Таким образом, Decimals имеют гораздо более высокую точность и обычно используются в денежных (финансовых) приложениях, требующих высокой степени точности. Но по производительности разумные Десятичные числа медленнее, чем двойные и плавающие типы. Двойные типы, вероятно, являются наиболее обычно используемым типом данных для реальных значений, за исключением обработки денег. Подробнее... Double vs Decimal

Rj

Ответ 9

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

Ответ 10

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

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

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

Ответ 11

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

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

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

.NET Framework 4

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

Ответ 12

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

Ответ 13

Зависит от того, что вам нужно.

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

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