Одно из лучших решений для определения количества цифр после десятичной точки показано в сообщении burn_LEGION.
Здесь я использую части из статьи форума STSdb: Количество цифр после десятичной точки.
В MSDN мы можем прочитать следующее объяснение:
"Десятичное число - это значение с плавающей запятой, состоящее из знака, числового значения, где каждая цифра в значении находится в диапазоне от 0 до 9, и коэффициента масштабирования, который указывает положение десятичной запятой с плавающей запятой, разделяющей целая и дробная части числового значения. "
А также:
«Двоичное представление десятичного значения состоит из 1-битового знака, 96-битного целого числа и коэффициента масштабирования, используемого для деления 96-битного целого числа и указания того, какая его часть является десятичной дробью. коэффициент масштабирования неявно представляет собой число 10, возведенное в степень в диапазоне от 0 до 28 ".
На внутреннем уровне десятичное значение представлено четырьмя целыми числами.
![Десятичное внутреннее представление](https://i.stack.imgur.com/RGIiZ.jpg)
Существует общедоступная функция GetBits для получения внутреннего представления. Функция возвращает массив int []:
[__DynamicallyInvokable]
public static int[] GetBits(decimal d)
{
return new int[] { d.lo, d.mid, d.hi, d.flags };
}
Четвертый элемент возвращаемого массива содержит коэффициент масштабирования и знак. И, как сообщает MSDN, коэффициент масштабирования неявно представляет собой число 10, возведенное в степень в диапазоне от 0 до 28. Это именно то, что нам нужно.
Таким образом, на основе всех вышеперечисленных исследований мы можем построить наш метод:
private const int SIGN_MASK = ~Int32.MinValue;
public static int GetDigits4(decimal value)
{
return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}
Здесь SIGN_MASK используется для игнорирования знака. После логического и мы также сдвинули результат на 16 бит вправо, чтобы получить фактический масштабный коэффициент. Это значение, наконец, указывает количество цифр после десятичной точки.
Обратите внимание, что здесь MSDN также сообщает, что коэффициент масштабирования также сохраняет любые конечные нули в десятичном числе. Завершающие нули не влияют на значение десятичного числа в арифметических операциях или операциях сравнения. Однако конечные нули могут быть обнаружены методом ToString, если применяется соответствующая строка формата.
Это решение выглядит лучшим, но подождите, это еще не все. Путем доступа к закрытым методам в C # мы можем использовать выражения для создания прямого доступа к полю флагов и избежать построения массива int:
public delegate int GetDigitsDelegate(ref Decimal value);
public class DecimalHelper
{
public static readonly DecimalHelper Instance = new DecimalHelper();
public readonly GetDigitsDelegate GetDigits;
public readonly Expression<GetDigitsDelegate> GetDigitsLambda;
public DecimalHelper()
{
GetDigitsLambda = CreateGetDigitsMethod();
GetDigits = GetDigitsLambda.Compile();
}
private Expression<GetDigitsDelegate> CreateGetDigitsMethod()
{
var value = Expression.Parameter(typeof(Decimal).MakeByRefType(), "value");
var digits = Expression.RightShift(
Expression.And(Expression.Field(value, "flags"), Expression.Constant(~Int32.MinValue, typeof(int))),
Expression.Constant(16, typeof(int)));
//return (value.flags & ~Int32.MinValue) >> 16
return Expression.Lambda<GetDigitsDelegate>(digits, value);
}
}
Этот скомпилированный код присваивается полю GetDigits. Обратите внимание, что функция получает десятичное значение как ref, поэтому фактическое копирование не выполняется - только ссылка на значение. Использовать функцию GetDigits из DecimalHelper просто:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
Это самый быстрый способ получить количество цифр после десятичной точки для десятичных значений.
person
Kristiyan Dimitrov
schedule
03.07.2014
19.0
на возврат1
- это деталь реализации, касающаяся внутренней памяти значения19.0
. Дело в том, что программа может хранить это как190×10⁻¹
,1900×10⁻²
или19000×10⁻³
. Все они равны. Тот факт, что он использует первое представление, когда ему задано значение19.0M
, и это раскрывается при использованииToString
без спецификатора формата, просто совпадение и радость. За исключением того, что это неприятно, когда люди полагаются на показатель степени в тех случаях, когда им не следует этого делать. - person ErikE   schedule 22.08.201519M
от19.0M
от19.00M
, вам необходимо создать новый класс, который объединяет базовое значение как одно свойство и число десятичных знаков как другое свойство. - person ErikE   schedule 22.08.2015whatever(654.32100m)
вернуть? - person Solomon Ucko   schedule 11.02.2020