簡體   English   中英

C# 檢查小數點是否超過 3 個小數位?

[英]C# Check if a decimal has more than 3 decimal places?

我有一種無法更改的情況:一個數據庫表(表 A)接受 6 個小數位,而另一個表(表 B)中的相關列只有 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 中的 Qty * Unit 是否有超過 3 位小數(第 1 行會失敗,第 2 行會通過):

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

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

您可以將四舍五入到小數點后 3 位的數字的值與原始值進行比較。

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

這適用於 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 的所有小數(對於真正的通用函數,您需要類似“大整數”的東西)。

static decimal CountDecimalPlaces(decimal dec)
{
    Console.Write("{0}: ", dec);
    int[] bits = Decimal.GetBits(dec);
    ulong lowInt = (uint)bits[0];
    ulong midInt = (uint)bits[1];
    int exponent = (bits[3] & 0x00FF0000) >> 16;
    int result = exponent;
    ulong lowDecimal = lowInt | (midInt << 32);
    while (result > 0 && (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));
}

這是一個非常簡單的一行代碼,用於獲取 Decimal 中的小數位數:

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

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

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

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

基礎知識是知道如何測試是否有小數位,這是通過將值與其舍入進行比較來完成的

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

然后,要計算 3 個小數位,您只需要對乘以 1000 的數字執行相同的操作:

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

到目前為止提出的所有解決方案都是不可擴展的......如果你永遠不會檢查 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;
}       

carlosfigueira 解決方案需要檢查 0 否則“while ((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));

另一個基於@RodH257 解決方案的選項,但作為擴展方法重新設計:

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

然后,您可以將其稱為:

If !(tableA.Qty * tableA.Unit).HasThisManyDecimalPlacesOrLess(3)) return;
    bool CountDecimalPlaces(decimal input)
    {
        return input*1000.0 == (int) (input*1000);
    }

可能有一種更優雅的方法來做到這一點,但我會嘗試

  1. a = 乘以 1000
  2. b = 截斷 a
  3. if (b != a) 那么額外的精度已經丟失
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

你能把它轉換成一個字符串然后只做一個 len 函數還是不能涵蓋你的情況?

后續問題:300.4 可以嗎?

這是我的版本:

public static int CountDecimalPlaces(decimal dec)
{
    var a = Math.Abs(dec);
    var x = a;
    var count = 1;
    while (x % 1 != 0)
    {
        x = a * new decimal(Math.Pow(10, count++));
    }

    var result = count - 1;

    return result;
}

我首先嘗試了@carlosfigueira/@Henrik Stenbæk ,但他們的版本不適用於324000.00m

測試:

Console.WriteLine(CountDecimalPlaces(0m)); //0
Console.WriteLine(CountDecimalPlaces(0.5m)); //1
Console.WriteLine(CountDecimalPlaces(10.51m)); //2
Console.WriteLine(CountDecimalPlaces(10.5123456978563m)); //13
Console.WriteLine(CountDecimalPlaces(324000.0001m)); //4
Console.WriteLine(CountDecimalPlaces(324000.0000m)); //0

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM