Một trong những giải pháp tốt nhất để tìm số chữ số sau dấu thập phân được hiển thị trong bài đăng của burn_LEGION .
Ở đây tôi đang sử dụng các phần từ một bài viết trên diễn đàn STSdb: Số chữ số sau dấu thập phân .
Trong MSDN, chúng ta có thể đọc phần giải thích sau:
"Số thập phân là giá trị dấu phẩy động bao gồm dấu, giá trị số trong đó mỗi chữ số trong giá trị nằm trong khoảng từ 0 đến 9 và hệ số tỷ lệ cho biết vị trí của dấu phẩy động phân tách tích phân và phân số các phần của giá trị số. "
Và ngoài ra:
"Biểu diễn nhị phân của giá trị Thập phân bao gồm dấu 1 bit, số nguyên 96 bit và hệ số tỷ lệ được sử dụng để chia số nguyên 96 bit và chỉ định phần nào của nó là phần thập phân. Hệ số tỷ lệ là ngầm định là số 10, được nâng lên thành số mũ trong khoảng từ 0 đến 28. "
Ở cấp nội bộ, giá trị thập phân được biểu thị bằng bốn giá trị số nguyên.
Có một chức năng GetBits có sẵn công khai để lấy đại diện nội bộ. Hàm trả về một mảng int []:
[__DynamicallyInvokable]
public static int[] GetBits(decimal d)
{
return new int[] { d.lo, d.mid, d.hi, d.flags };
}
Phần tử thứ tư của mảng được trả về chứa hệ số tỷ lệ và dấu hiệu. Và như MSDN nói rằng hệ số tỷ lệ ngầm định là số 10, được nâng lên thành số mũ từ 0 đến 28. Đây chính là thứ chúng ta cần.
Do đó, dựa trên tất cả các điều tra ở trên, chúng ta có thể xây dựng phương pháp của mình:
private const int SIGN_MASK = ~Int32.MinValue;
public static int GetDigits4(decimal value)
{
return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}
Ở đây, SIGN_MASK được sử dụng để bỏ qua dấu hiệu. Sau khi logic và chúng tôi cũng đã chuyển kết quả có 16 bit sang bên phải để nhận hệ số tỷ lệ thực tế. Giá trị này, cuối cùng, cho biết số chữ số sau dấu thập phân.
Lưu ý rằng ở đây MSDN cũng cho biết hệ số tỷ lệ cũng bảo toàn bất kỳ số không ở cuối trong số Thập phân. Các số không ở cuối không ảnh hưởng đến giá trị của một số Thập phân trong các phép toán số học hoặc so sánh. Tuy nhiên, các số không ở cuối có thể được tiết lộ bởi phương thức ToString nếu một chuỗi định dạng thích hợp được áp dụng.
Giải pháp này có vẻ là giải pháp tốt nhất, nhưng chờ đợi, còn nhiều hơn thế. Bằng cách truy cập các phương thức private trong C #, chúng ta có thể sử dụng các biểu thức để xây dựng quyền truy cập trực tiếp vào trường flags và tránh xây dựng mảng 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);
}
}
Mã đã biên dịch này được gán cho trường GetDigits. Lưu ý rằng hàm nhận giá trị thập phân dưới dạng ref, vì vậy không có quá trình sao chép thực tế nào được thực hiện - chỉ tham chiếu đến giá trị. Sử dụng hàm GetDigits từ DecimalHelper thật dễ dàng:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
Đây là phương pháp nhanh nhất có thể để nhận số chữ số sau dấu thập phân cho các giá trị thập phân.