[英]custom method for returning decimal places shows odd behavior
我正在編寫一個簡單的方法來計算小數值中的小數位數。 該方法如下所示:
public int GetDecimalPlaces(decimal decimalNumber) {
try {
int decimalPlaces = 1;
double powers = 10.0;
if (decimalNumber > 0.0m) {
while (((double)decimalNumber * powers) % 1 != 0.0) {
powers *= 10.0;
++decimalPlaces;
}
}
return decimalPlaces;
我已經針對一些測試值運行它以確保一切正常但是在最后一個上得到了一些非常奇怪的行為:
int test = GetDecimalPlaces(0.1m);
int test2 = GetDecimalPlaces(0.01m);
int test3 = GetDecimalPlaces(0.001m);
int test4 = GetDecimalPlaces(0.0000000001m);
int test5 = GetDecimalPlaces(0.00000000010000000001m);
int test6 = GetDecimalPlaces(0.0000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001m);
測試1-5工作正常,但test6返回23.我知道傳入的值超過了最大小數精度,但為什么23? 我發現奇怪的另一件事是當我在來自test6的調用之后在GetDecimalPlaces方法中放置一個斷點時,方法中的decimalNumber的值來自test5(20個小數位)的相同值,即使該值傳入有20個小數位23返回。
也許只是因為我傳遞了一個數字,這個數字有太多的小數位並且事情變得不穩定但是我想確保我不會遺漏一些根本錯誤的東西,這可能會導致其他值的計算失敗。路。
你實際測試的數字是這樣的:
0.0000000001000000000100000000
這是最接近的精確十進制值,為0.0000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001。
所以正確的答案實際上是20.然而,你的代碼給你23因為你使用二進制浮點運算,沒有明顯的原因。 這將會在計算中引入錯誤,完全不必要。 如果你改為一致地使用十進制,那很好:
public static int GetDecimalPlaces(decimal decimalNumber) {
int decimalPlaces = 1;
decimal powers = 10.0m;
if (decimalNumber > 0.0m) {
while ((decimalNumber * powers) % 1 != 0.0m) {
powers *= 10.0m;
++decimalPlaces;
}
}
return decimalPlaces;
}
(建議)您可以這樣計算:
public static int GetDecimalPlaces(decimal decimalNumber)
{
var s = decimalNumber.ToString();
return s.Substring(s.IndexOf(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) + 1).Length;
}
還有另一種方法可以做到這一點,可能它的工作速度更快,因為只有在十進制數有“尾隨零”問題時才使用余數運算。
基本理念:
在.NET中,任何小數都存儲在表單的內存中
m * Math.Power(10,-p)
其中m是尾數(96位大小),p是順序(值從0到28)。
decimal.GetBits方法從十進制結構中檢索此表示形式,並將其作為int(長度為4)的數組返回。
使用這些數據我們可以構造另一個小數。 如果我們只使用尾數,沒有“Math.Power(10,-p)”部分,結果將是一個整數十進制。 如果這個整數十進制數可以被10整除,那么我們的源數有一個或多個尾隨零。
所以這是我的代碼
static int GetDecimalPlaces(decimal value)
{
// getting raw decimal structure
var raw = decimal.GetBits(value);
// getting current decimal point position
int decimalPoint = (raw[3] >> 16) & 0xFF;
// using raw data to create integral decimal with the same mantissa
// (note: it always will be absolute value because I do not analyze
// the sign information of source number)
decimal integral = new decimal(raw[0], raw[1], raw[2], false, 0);
// disposing from trailing zeros
while (integral > 0 && integral % 10 == 0)
{
decimalPoint--;
integral /= 10;
}
// returning the answer
return decimalPoint;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.