Подтвердить что ты не робот

Является ли "десятичная" арифметика .NET независимой от платформы/архитектуры?

Недавно я спросил о System.Double и сказал, что вычисления могут отличаться в зависимости от платформы/архитектуры. К сожалению, я не могу найти какую-либо информацию, чтобы сообщить мне, относится ли это к System.Decimal.

Я гарантированно получаю точно такой же результат для любого конкретного вычисления decimal независимо от платформы/архитектуры?

4b9b3361

Ответ 1

Я гарантированно получаю точно такой же результат для любого конкретного десятичного вычисления независимо от платформы/архитектуры?

В спецификации С# 4 ясно, что полученное вами значение будет вычисляться одинаково на любой платформе.

Как отмечает LukeH, версия ECMA спецификации С# 2 предоставляет свободу действий для соответствия реализациям, чтобы обеспечить более высокую точность, поэтому реализация С# 2.0 на другой платформе может обеспечить ответ с более высокой точностью.

Для целей этого ответа я просто обсужу указанное поведение С# 4.0.

Спецификация С# 4.0 говорит:


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


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

Однако обратите внимание на скобки и последнее предложение об нулях. Возможно, неясно, зачем нужна эта информация.

Одна из странностей десятичной системы состоит в том, что почти каждая величина имеет более одного возможного представления. Рассмотрим точное значение 123.456. Десятичная комбинация состоит из 96-битного целого числа, знака с 1 битом и восьмибитового показателя, который представляет число от -28 до 28. Это означает, что точное значение 123.456 может быть представлено десятичными знаками 123456 x 10 - 3 или 1234560 x 10 -4 или 12345600 x 10 -5. Значение шкалы.

Спецификация С# также определяет, как вычисляется информация о масштабе. Литерал 123.456m будет закодирован как 123456 x 10 -3 а 123.4560m будет закодирован как 1234560 x 10 -4.

Наблюдайте за эффектами этой функции в действии:

decimal d1 = 111.111000m;
decimal d2 = 111.111m;
decimal d3 = d1 + d1;
decimal d4 = d2 + d2;
decimal d5 = d1 + d2;
Console.WriteLine(d1);
Console.WriteLine(d2);
Console.WriteLine(d3);
Console.WriteLine(d4);
Console.WriteLine(d5);
Console.WriteLine(d3 == d4);
Console.WriteLine(d4 == d5);
Console.WriteLine(d5 == d3);

Это создает

111.111000
111.111
222.222000
222.222
222.222000
True
True
True

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

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

Я также отмечаю, что формат внутренней памяти десятичного числа в реализации CLR составляет 128 бит, подразделяемый на: 16 неиспользуемых битов, 8 бит шкалы, еще 7 неиспользуемых битов, 1 знаковый бит и 96 бит мантиссы. Точная компоновка этих бит в памяти не определяется спецификацией, и если другая реализация хочет записать дополнительную информацию в эти 23 неиспользуемых бита для своих собственных целей, она может это сделать. В реализации CLR неиспользуемые биты всегда должны быть равны нулю.

Ответ 2

Тип decimal представлен в количестве, равном base-10, используя структуру (содержащую целые числа, я полагаю), в отличие от double и других типов с плавающей запятой, которые представляют собой нецелые значения в base- 2. Таким образом, decimal представляют собой точные представления значений base-10 в рамках стандартизованной точности для любой архитектуры. Это справедливо для любой архитектуры, использующей правильную реализацию спецификации .NET.

Итак, чтобы ответить на ваш вопрос, так как поведение decimal стандартизировано таким образом в спецификации, значения decimal должны быть одинаковыми для любой архитектуры, соответствующей этой спецификации. Если они не соответствуют этой спецификации, то они на самом деле не .NET.

"Десятичный" тип .NET против "Float" и "Double" C/С++ Type

Ответ 3

Несмотря на то, что формат типов с плавающей точкой четко определен, вычисления с плавающей запятой могут действительно иметь разные результаты в зависимости от архитектуры, как указано в разделе 4.1.6 of спецификация С#:

Операции с плавающей запятой могут быть выполняются с большей точностью, чем тип результата операции. Для Например, некоторые аппаратные архитектуры поддерживать "расширенный" или "длинный двойной", тип с плавающей точкой с большим диапазоном и точность, чем двойной тип, и неявно выполнять все операции с плавающей запятой, используя это более высокий тип точности. Только в чрезмерная стоимость исполнения может аппаратные архитектуры выполнять операции с плавающей запятой меньшей точности, а не требуют исполнения для отказа как производительность, так и точность, С# позволяет использовать более высокий тип точности используется для всех плавающих точек операции.

В то время как тип decimal подлежит аппроксимации для того, чтобы значение представлялось в его конечном диапазоне, диапазон, по определению, определяется как подходящий для финансовых и денежных расчетов. Поэтому он имеет более высокую точность (и меньший диапазон), чем float или double. Он также более четко определен, чем другие типы с плавающей точкой, так что он выглядит не зависящим от платформы (см. Раздел 4.1.7 - я подозреваю эта независимость платформы больше, потому что не существует стандартной аппаратной поддержки типов с размером и точностью decimal, а не из-за самого типа, поэтому это может измениться с будущими спецификациями и аппаратными архитектурами).

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

Ответ 4

Чтение спецификации предполагает, что decimal - like float и double - может быть допущено некоторое отклонение в его реализации, если оно соответствует определенным минимальным стандартам.

Вот несколько выдержек из ECMA С# spec (раздел 11.1.7). Все выделение жирным шрифтом принадлежит мне.

Тип decimal может представлять значения , включая, в диапазон 1 x 10 -28 через 1 x 10 28 с не менее 28 значащих цифр.

Конечный набор значений типа decimal имеет вид (-1) s x c x 10 -e где знак s 0 или 1, коэффициент c задается 0 <= c < Cmax, и масштаб e таков, что Emin <= e <= Emax, где Cmax не менее 1 x 10 28 Emin < = 0 и Emax > = 28. Тип decimal не обязательноподдерживать подписанные нули, бесконечности или NaN.

Для decimal с абсолютным значением, меньшим 1.0m, значение является точным до не менее 28 th десятичной место. Для decimal с абсолютным значением больше или равное 1.0m, значение является точным до не менее 28 цифр.

Обратите внимание, что формулировка Microsoft С# spec (раздел 4.1.7) существенно отличается от формулировки спецификации ECMA. Похоже, что более строгое поведение decimal больше блокируется.