小数点以下の桁数を見つけるための最良の解決策の1つは、burning_LEGIONの投稿に示されています。
ここでは、STSdbフォーラムの記事の一部を使用しています:小数点以下の桁数。
MSDNでは、次の説明を読むことができます。
「10進数は、符号、値の各桁が0〜9の範囲の数値、および整数と小数を分離する浮動小数点の位置を示すスケーリング係数で構成される浮動小数点値です。数値の一部。」
そしてまた:
「10進値の2進表現は、1ビットの符号、96ビットの整数、および96ビットの整数を除算してその小数部を指定するために使用されるスケーリング係数で構成されます。スケーリング係数は次のとおりです。暗黙的に数値10は、0から28の範囲の指数に引き上げられます。」
内部レベルでは、10進値は4つの整数値で表されます。
内部表現を取得するための公開されているGetBits関数があります。この関数はint []配列を返します。
[__DynamicallyInvokable]
public static int[] GetBits(decimal d)
{
return new int[] { d.lo, d.mid, d.hi, d.flags };
}
返される配列の4番目の要素には、スケール係数と符号が含まれています。そして、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は、スケーリング係数も10進数の後続ゼロを保持すると述べていることに注意してください。末尾のゼロは、算術演算または比較演算の10進数の値には影響しません。ただし、適切なフォーマット文字列が適用されている場合、後続のゼロは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 Expression.Lambda<GetDigitsDelegate>(digits, value);
}
}
このコンパイルされたコードは、GetDigitsフィールドに割り当てられます。関数は10進値をrefとして受け取るため、実際のコピーは実行されず、値への参照のみが実行されることに注意してください。DecimalHelperからGetDigits関数を使用するのは簡単です。
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
これは、10進値の小数点以下の桁数を取得するための可能な限り最速の方法です。