2011-05-23 25 views
11

我有一種情況是無法更改的:一個數據庫表(表A)接受6個小數位,而另一個表(表B)中的相關列只有3個小數位。C#檢查一個小數是否有超過3個小數位?

我需要從A複製到B,但是如果A有3個以上的小數位,額外的數據將會丟失。我不能改變表格的定義,但我可以添加一個解決方法。所以我試圖找出如何檢查一個小數是否有超過3個小數位?

Table A 
Id, Qty, Unit(=6dp) 
1, 1,  0.00025 
2, 4000, 0.00025 

Table B 
Id, TotalQty(=3dp) 

我希望能夠找出是否從表A數量*單位已超過3位小數(第1行會失敗,第2行會通過):

if (CountDecimalPlaces(tableA.Qty * tableA.Unit) > 3) 
{ 
    return false; 
} 
tableB.TotalQty = tableA.Qty * tableA.Unit; 

我將如何實現CountDecimalPlaces(decimal value) {}函數?

回答

11

這適用於小數點後3位,它可以適用於一個通用的解決方案:

static bool LessThan3DecimalPlaces(decimal dec) 
{ 
    decimal value = dec * 1000; 
    return value == Math.Floor(value); 
} 
static void Test() 
{ 
    Console.WriteLine(LessThan3DecimalPlaces(1m * 0.00025m)); 
    Console.WriteLine(LessThan3DecimalPlaces(4000m * 0.00025m)); 
} 

對於真正的通用解決方案,您需要「解構」其零件中的小數值 - 請參閱Decimal.GetBits以獲取更多信息。

更新:這是一個通用解決方案的簡單實現,它適用於整數部分小於long.MaxValue的所有小數(對於trully泛型函數,您需要類似「大整數」的東西)。

static decimal CountDecimalPlaces(decimal dec) 
{ 
    int[] bits = Decimal.GetBits(dec); 
    int exponent = bits[3] >> 16; 
    int result = exponent; 
    long lowDecimal = bits[0] | (bits[1] >> 8); 
    while ((lowDecimal % 10) == 0) 
    { 
     result--; 
     lowDecimal /= 10; 
    } 

    return result; 
} 
static void Foo() 
{ 
    Console.WriteLine(CountDecimalPlaces(1.6m)); 
    Console.WriteLine(CountDecimalPlaces(1.600m)); 
    Console.WriteLine(CountDecimalPlaces(decimal.MaxValue)); 
    Console.WriteLine(CountDecimalPlaces(1m * 0.00025m)); 
    Console.WriteLine(CountDecimalPlaces(4000m * 0.00025m)); 
} 
+0

+1 - 我認爲LessThan3DecimalPlaces應該LessThanOrEqualTo3DecimalPlaces :) – VoodooChild 2012-03-08 06:03:32

+5

如果使用dec調用CountDecimalPlaces將會形成一個無限循環== 0 – 2012-08-07 10:46:50

+0

這似乎不適用於小數點後8位 - 返回7 – Phil 2014-04-14 15:03:08

1
bool CountDecimalPlaces(decimal input) 
    { 
     return input*1000.0 == (int) (input*1000); 
    } 
1

有可能是一個更優雅的方式來做到這一點,但是從我的頭頂,我會嘗試

  1. A =乘以1000
  2. B =截斷
  3. 如果(b!= a)那麼就會有額外的精度丟失
0

你能把它轉換成字符串,只是做一個len函數或者不會cov呃你的情況?

後續問題: 會300.4好嗎?

3

的基礎是要知道如何測試是否有小數位,這是通過比較值的四捨五入

double number; 
bool hasDecimals = number == (int) number; 

然後,做了數小數點後3位,你只需要做同樣的爲您的數字乘以1000:

bool hasMoreThan3decimals = number*1000 != (int) (number * 1000) 
2

所有迄今提出的解決方案是不可擴展......很好,如果你永遠不會比檢查3以外的價值,但我更喜歡這一點,因爲如果要求改變代碼來處理它已經是書面。此解決方案也不會溢出。

int GetDecimalCount(decimal val) 
{ 
    if(val == val*10) 
    { 
     return int.MaxValue; // no decimal.Epsilon I don't use this type enough to know why... this will work 
    } 

    int decimalCount = 0; 
    while(val != Math.Floor(val)) 
    { 
     val = (val - Math.Floor(val)) * 10; 
     decimalCount++; 
    } 
    return decimalCount; 
}  
40

您可以將四捨五入到小數點後三位的數值與原始值進行比較。

if (Decimal.Round(valueDecimal, 3) != valueDecimal) 
{ 
    //Too many decimals 
} 
+0

易於閱讀且易於更改小數點位數。一段精美優雅的代碼,謝謝! – crabCRUSHERclamCOLLECTOR 2012-06-20 14:56:35

+0

只是我在閱讀已接受的解決方案後添加的答案,但您已擁有! – SharpC 2015-11-20 13:13:02

2

carlosfigueira解決方案將需要檢查,否則爲0 「而((lowDecimal%10)== 0)」 將產生一個無窮循環時與DEC = 0

static decimal CountDecimalPlaces(decimal dec) 
    { 
     if (dec == 0) 
      return 0; 
     int[] bits = Decimal.GetBits(dec); 
     int exponent = bits[3] >> 16; 
     int result = exponent; 
     long lowDecimal = bits[0] | (bits[1] >> 8); 
     while ((lowDecimal % 10) == 0) 
     { 
      result--; 
      lowDecimal /= 10; 
     } 
     return result; 
    } 

    Assert.AreEqual(0, DecimalHelper.CountDecimalPlaces(0m));  
    Assert.AreEqual(1, DecimalHelper.CountDecimalPlaces(0.5m)); 
    Assert.AreEqual(2, DecimalHelper.CountDecimalPlaces(10.51m)); 
    Assert.AreEqual(13, DecimalHelper.CountDecimalPlaces(10.5123456978563m)); 
0
Public Function getDecimalCount(decWork As Decimal) As Integer 

    Dim intDecimalCount As Int32 = 0 
    Dim strDecAbs As String = decWork.ToString.Trim("0") 

    intDecimalCount = strDecAbs.Substring(strDecAbs.IndexOf(".")).Length -1 

    Return intDecimalCount 

End Function 
+0

您能否向您的代碼添加一些解決方案的解釋? – jonsca 2012-09-24 12:35:59

+0

我不在乎額外的零,所以,decWork.ToString.Trim(「0」) 刪除它們,.50變成了.5 strDecAbs.SubstringstrDecAbs.Substring(strDecAbs.IndexOf(「。」))。長度-1給出從小數點開始的字符串的長度,因爲包含了這個點,所以我減去1。 – Ricardo 2012-09-24 15:38:01

2

稱爲將小數點後3位的數字乘以10的3次方將得到一個沒有小數位的數字。當模數% 1 == 0時是整數。所以,我想出了這個...

bool hasMoreThanNDecimals(decimal d, int n) 
{ 
    return !(d * (decimal)Math.Pow(10, n) % 1 == 0); 
} 

返回true時n小於(不等於)小數位的數量。

1

這是一個非常簡單的一行代碼來獲得小數數在十進制:

decimal myDecimal = 1.000000021300010000001m; 
byte decimals = (byte)((Decimal.GetBits(myDecimal)[3] >> 16) & 0x7F); 
0

還有一個選項基於@ RodH257的解決方案,而是重新設計爲一個擴展方法:

public static bool HasThisManyDecimalPlacesOrLess(this decimal value, int noDecimalPlaces) 
{ 
    return (Decimal.Round(value, noDecimalPlaces) == value); 
} 

然後,您可以調用爲:

If !(tableA.Qty * tableA.Unit).HasThisManyDecimalPlacesOrLess(3)) return; 
相關問題