Есть ли какая-либо практическая разница между десятичными значениями .net 1m и 1.0000m?

Существует ли какая-либо практическая разница между десятичными значениями .net 1m и 1.0000m?

Внутреннее хранилище отличается:

1m      : 0x00000001 0x00000000 0x00000000 0x00000000
1.0000m : 0x000186a0 0x00000000 0x00000000 0x00050000

Но существует ли ситуация, когда знание "значащих цифр" будет использоваться методом в BCL?

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

Ответы

Ответ 1

Единственная причина, по которой я могу думать, - это так вызывать `ToString возвращает точное текстовое представление в исходном коде.

Console.WriteLine(1m); // 1
Console.WriteLine(1.000m); // 1.000

Ответ 2

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

integer / 10^scale

Внутренне тип Decimal представлен как 4 Int32, более подробно см. документацию Decimal.GetBits. Таким образом, GetBits возвращает массив из 4 Int32, где каждый элемент представляет собой следующую часть десятичной кодировки

Element 0,1,2 - Represent the low, middle and high 32 bits on the 96 bit integer
Element 3     - Bits 0-15 Unused
                Bits 16-23 exponent which is the power of 10 to divide the integer by
                Bits 24-30 Unused 
                Bit 31 the sign where 0 is positive and 1 is negative

Итак, в вашем примере очень просто поставить, когда 1.0000m закодирован как десятичное, фактическое представление 10000 / 10^4, в то время как 1m представляется как 1 / 10^0 математически одно и то же значение, просто закодированное по-разному.

Если вы используете собственные .NET-операторы для десятичного типа и не манипулируете/не сравниваете бит/байты, вы должны быть в безопасности.

Вы также заметите, что преобразования строк также учитывают это двоичное представление и производят разные строки, поэтому вам нужно быть осторожными в этом случае, если вы когда-либо полагаетесь на строковое представление.

Ответ 3

Тип decimal отслеживает масштаб, потому что он важен в арифметике. Если вы делаете длинное умножение, вручную, из двух чисел -— например, 3,14 * 5,00 — результат имеет 6 цифр точности и шкалу 4.

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

  3.14
* 5.00
------
  0000 -- 0 * 314 (0 in the one place)
 00000 -- 0 * 314 (0 in the 10 place)
157000 -- 5 * 314 (5 in the 100 place)
------
157000

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

15.7000

Этот результат, эквивалентный по значению 15.7, более точный, чем значение 15.7. Значение 15.7000 имеет 6 цифр точности и шкалу 4; 15.7 имеет 3 цифры точности и шкалу от 1.

Если кто-то пытается выполнить арифметику точности, важно отслеживать точность и масштаб ваших значений и результатов, поскольку он говорит вам что-то о точности ваших результатов (обратите внимание, что точность не так же, как точность: измерить что-то с линейкой, градуированной в 1/10 дюйма и наилучшим образом вы можете сказать о полученном измерении, независимо от того, сколько конечных нулей вы ставите справа от десятичной точки, так это то, что она в лучшем случае соответствует 1/10-й дюйм. Другой способ сделать это - сказать, что ваши измерения точны, в лучшем случае, в пределах +/- 5/100th от указанного значения.