One of the best solutions for finding the number of digits after a decimal point is shown in the burn_LEGION message.
Here I use the details from the STSdb forum article: The number of digits after the decimal point .
At MSDN, we can read the following explanation:
"A decimal number is a floating point value that consists of a sign, a numerical value, where each digit in the value is in the range from 0 to 9, and a scaling factor that indicates the position of the floating decimal point that separates the integral and fractional parts of the numerical value . "
As well as:
“The binary representation of a decimal value consists of a 1-bit character, a 96-bit integer and a scaling factor used to separate a 96-bit integer and indicate which part it is a decimal fraction. The scaling factor is implicitly equal to the number 10 raised to an exponent in the range from 0 to 28. "
At the internal level, the decimal value is represented by four integer values.

There is a publicly available GetBits function for retrieving an internal view. The function returns an array int []:
[__DynamicallyInvokable] public static int[] GetBits(decimal d) { return new int[] { d.lo, d.mid, d.hi, d.flags }; }
The fourth element of the returned array contains a scale factor and a sign. And as MSDN says that the scaling factor is implicitly equal to 10, it increases to an exponent from 0 to 28. This is exactly what we need.
Thus, based on all the above studies, we can build our method:
private const int SIGN_MASK = ~Int32.MinValue; public static int GetDigits4(decimal value) { return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16; }
Here, the SIGN_MASK character is used to ignore the character. After the logical one, we also shifted the result from 16 bits to the right to get the actual scale factor. This value finally indicates the number of digits after the decimal point.
Note that here MSDN also says that the scaling factor also stores any trailing zeros in decimal. Trailing zeros do not affect the decimal value in arithmetic or comparative operations. However, trailing zeros can be detected by the ToString method if the appropriate format string is used.
These solutions look like the best, but wait, there are more. Using access to private methods in C # , we can use expressions to create direct access to the flags field and avoid building an int array:
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)));
This compiled code is assigned to the GetDigits field. Note that the function takes the decimal value as ref, so the actual copying is not performed - only a reference to the value. Using the GetDigits function of DecimalHelper is very simple:
decimal value = 3.14159m; int digits = DecimalHelper.Instance.GetDigits(ref value);
This is the fastest way to get the number of digits after the decimal point for decimal values.