2010-09-10 136 views
15

更新

好的,經過一番調查,並且在很大程度上感謝Jon和Hans提供的有用答案,這就是我能夠把它放在一起。到目前爲止,我認爲它似乎運作良好。當然,我不打賭我的生命在於它的完全正確性。有沒有辦法獲得小數的「有效數字」?

public static int GetSignificantDigitCount(this decimal value) 
{ 
    /* So, the decimal type is basically represented as a fraction of two 
    * integers: a numerator that can be anything, and a denominator that is 
    * some power of 10. 
    * 
    * For example, the following numbers are represented by 
    * the corresponding fractions: 
    * 
    * VALUE NUMERATOR DENOMINATOR 
    * 1  1   1 
    * 1.0  10   10 
    * 1.012 1012  1000 
    * 0.04  4   100 
    * 12.01 1201  100 
    * 
    * So basically, if the magnitude is greater than or equal to one, 
    * the number of digits is the number of digits in the numerator. 
    * If it's less than one, the number of digits is the number of digits 
    * in the denominator. 
    */ 

    int[] bits = decimal.GetBits(value); 

    if (value >= 1M || value <= -1M) 
    { 
     int highPart = bits[2]; 
     int middlePart = bits[1]; 
     int lowPart = bits[0]; 

     decimal num = new decimal(lowPart, middlePart, highPart, false, 0); 

     int exponent = (int)Math.Ceiling(Math.Log10((double)num)); 

     return exponent; 
    } 
    else 
    { 
     int scalePart = bits[3]; 

     // Accoring to MSDN, the exponent is represented by 
     // bits 16-23 (the 2nd word): 
     // http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx 
     int exponent = (scalePart & 0x00FF0000) >> 16; 

     return exponent + 1; 
    } 
} 

我還沒有完全測試過它。這裏有幾個樣品輸入/輸出,但:

 
Value   Precision 
0    1 digit(s). 
0.000   4 digit(s). 
1.23   3 digit(s). 
12.324   5 digit(s). 
1.2300   5 digit(s). 
-5    1 digit(s). 
-5.01   3 digit(s). 
-0.012   4 digit(s). 
-0.100   4 digit(s). 
0.0   2 digit(s). 
10443.31  7 digit(s). 
-130.340  6 digit(s). 
-80.8000  6 digit(s). 

使用此代碼,我想我會做這樣的事情完成我的目標:

public static decimal DivideUsingLesserPrecision(decimal x, decimal y) 
{ 
    int xDigitCount = x.GetSignificantDigitCount(); 
    int yDigitCount = y.GetSignificantDigitCount(); 

    int lesserPrecision = System.Math.Min(xDigitCount, yDigitCount); 

    return System.Math.Round(x/y, lesserPrecision); 
} 

我還沒有真正完成工作通過這個,但是。任何想分享想法的人:這將是非常感謝!


原始的問題

假設我有寫這樣的代碼:

decimal a = 1.23M; 
decimal b = 1.23000M; 

Console.WriteLine(a); 
Console.WriteLine(b); 

上面會輸出:

 
1.23 
1.23000 

我覺得這也適用,如果我用decimal.Parse("1.23")a and decimal.Parse("1.23000") for b(whi ch表示該問題適用於程序收到用戶輸入的情況)。

所以很清楚,decimal值是以某種方式「知道」我將其稱爲精度。但是,除ToString本身外,我沒有看到decimal類型的成員提供任何訪問此方法的方法。

假設我想要乘以兩個decimal值並將結果修剪爲精度較低的參數。換句話說:

decimal a = 123.4M; 
decimal b = 5.6789M; 

decimal x = a/b; 

Console.WriteLine(x); 

以上輸出:

 
21.729560302171195125816619416 

什麼我問的是:我怎麼能寫會返回21.73替代的方法(因爲123.4M有四個顯著數字)?

要清楚:我意識到我可以在兩個參數上調用ToString,計算每個字符串中的有效數字,並使用此數字來計算結果。如果可能,我正在尋找不同的方式。

(我也意識到,在你處理顯著數字大多數情況下,你可能並不需要使用的decimal類型。但是,我這麼問是因爲,正如我在開始提到的, decimal類型出現包含有關精度的信息,而double並不如我所知。)

+2

我不知道我想知道這個,直到你問它+1! – msarchet 2010-09-10 14:35:06

+3

您的功能對某些輸入無法正常工作。例如,-0.012只有2位有效數字 - 不是4. – 2011-03-29 18:11:06

+0

@JamesJones顯然他指的是有效數字的不同概念,而不是數學課程中學習的數字。也許「使用數字」這個名字會更有意義。 – ErikE 2015-08-21 22:43:29

回答

3

您可以使用Decimal.GetBits來獲取原始數據,並從中獲取原始數據。

不幸的是,我現在沒有時間寫示例代碼 - 如果您使用的是.NET 4,您可能會想要使用BigInteger進行一些操作 - 但希望這會讓你走。只要計算出精度,然後在原始結果上調用Math.Round就可能是一個好的開始。

+0

即使我還沒有完成解決方案,我仍然接受這個答案,因爲它肯定給我提供了我需要的信息。 (不幸的是,我希望能比GetBits更容易訪問;但看起來這是我們最好的工作!) – 2010-09-10 15:08:22

3

是的,與浮點類型不同,System.Decimal跟蹤文字中的數字位數。這是decimal.Parse()的一個特性,無論你自己執行代碼還是編譯器在解析程序中的文字時執行。您可以恢復這些信息,查看我在this thread的回答中的代碼。

在對數值進行數學運算後恢復有效數字的數量會使我產生長時間的影響。不知道運營商是否保留它們,請讓我們知道你發現了什麼。

+3

'decimal' *是*浮點類型:) – 2010-09-10 15:10:45

+0

是的,我很漂亮確保信息不被保留。這是有道理的 - 我的意思是,考慮到像「1M/4M」這樣的顯然應該是「0.25M」而不是「0.3M」。無論如何,我解決了這個問題,並提出了一些類似於解決方案的解決方案(完全與我在鏈接答案中看到的相同位置混淆)!...我的代碼非常難看,但是。如果您稍後獲得免費分鐘,我一定會感謝您對問題頂部的更新。 – 2010-09-10 15:11:46

0

上面的解決方案快速下降,像1200這樣的數值有效位數是2,但函數返回4.也許有一個一致的方式來處理這種情況,即檢查以確保返回值確實不包括整數部分中沒有小數部分的尾隨零。

相關問題