Ondalık noktadan sonraki basamak sayısını bulmanın en iyi çözümlerinden biri, yanan_LEGION'un gönderisinde gösterilmiştir .
Burada bir STSdb forum makalesinden bölümler kullanıyorum: Ondalık noktadan sonraki hane sayısı .
MSDN'de aşağıdaki açıklamayı okuyabiliriz:
"Ondalık sayı, bir işaretten, değerdeki her basamağın 0 ile 9 arasında değiştiği bir sayısal değerden ve integral ile kesirliyi ayıran kayan bir ondalık noktanın konumunu gösteren bir ölçekleme faktöründen oluşan bir kayan nokta değeridir. sayısal değerin bölümleri. "
Ve ayrıca:
"Ondalık değerin ikili gösterimi, 1 bitlik bir işaret, 96 bitlik bir tam sayı ve 96 bitlik tamsayıyı bölmek ve bunun hangi bölümünün ondalık kesir olduğunu belirlemek için kullanılan bir ölçekleme faktöründen oluşur. Ölçeklendirme faktörü şu şekildedir: örtük olarak 10 sayısı, 0 ile 28 arasında bir üsse yükseltildi. "
Dahili düzeyde ondalık değer dört tam sayı değeriyle temsil edilir.
Dahili gösterimi elde etmek için halka açık bir GetBits işlevi vardır. İşlev bir int [] dizisi döndürür:
[__DynamicallyInvokable]
public static int[] GetBits(decimal d)
{
return new int[] { d.lo, d.mid, d.hi, d.flags };
}
Döndürülen dizinin dördüncü öğesi bir ölçek faktörü ve bir işaret içerir. Ve MSDN'nin dediği gibi, ölçeklendirme faktörü örtük olarak 10 sayısıdır ve 0 ile 28 arasında değişen bir üsse yükseltilir. Tam da ihtiyacımız olan şey budur.
Böylece, yukarıdaki tüm araştırmalara dayanarak yöntemimizi oluşturabiliriz:
private const int SIGN_MASK = ~Int32.MinValue;
public static int GetDigits4(decimal value)
{
return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}
Burada işareti yok saymak için SIGN_MASK kullanılır. Mantıksal sonra ve biz de gerçek ölçek faktörünü almak için sonucu 16 bit sağa kaydırdık. Son olarak bu değer, ondalık noktadan sonraki basamak sayısını gösterir.
Burada MSDN'nin ayrıca ölçeklendirme faktörünün de ondalık sayıdaki sondaki sıfırları koruduğunu söylediğine dikkat edin. Sondaki sıfırlar, aritmetik veya karşılaştırma işlemlerinde Ondalık sayının değerini etkilemez. Ancak, uygun bir biçim dizesi uygulanırsa, sondaki sıfırlar ToString yöntemi tarafından ortaya çıkarılabilir.
Bu çözümler en iyisine benziyor, ama bekleyin, dahası var. By C # özel yöntemler erişen biz bayraklar alanına doğrudan erişim oluşturmak ve int dizi inşa önlemek için ifadeleri kullanabilirsiniz:
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);
}
}
Derlenen bu kod GetDigits alanına atanır. Fonksiyonun ondalık değeri ref olarak aldığına dikkat edin, bu nedenle gerçek bir kopyalama gerçekleştirilmez - sadece değere bir referans. DecimalHelper'daki GetDigits işlevini kullanmak kolaydır:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
Bu, ondalık değerler için ondalık noktadan sonra basamak sayısını elde etmenin mümkün olan en hızlı yöntemidir.