【问题标题】:C# Check if a decimal has more than 3 decimal places?C#检查一个小数是否有超过3个小数位?
【发布时间】:2011-05-23 02:21:40
【问题描述】:

我有一个无法更改的情况:一个数据库表(表 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) {} 函数?

【问题讨论】:

    标签: c# decimal


    【解决方案1】:

    您可以将四舍五入到小数点后 3 位的数值与原始数值进行比较。

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

    【讨论】:

    • 易于阅读且易于更改小数位数。一段漂亮优雅的代码,谢谢!
    • 只是我在阅读接受的解决方案后要添加的答案,但你已经有了!
    【解决方案2】:

    这适用于小数点后 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));
    }
    

    【讨论】:

    • +1 - 我认为 LessThan3DecimalPlaces 应该是 LessThanOrEqualTo3DecimalPlaces :)
    • CountDecimalPlaces 如果使用 dec == 0 调用,将形成一个无限循环
    • @LuisLavieri / HenrikStenbæk 不错;我已经更新了函数来解决这些情况。
    【解决方案3】:

    这是一个非常简单的一行代码,用于获取十进制中的小数:

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

    【讨论】:

    • 这应该是答案,你只是迟到了。做得很好。出于好奇,为什么是 (byte) 而不是 (int) 什么的?
    【解决方案4】:

    将一个小数点后 3 位的数字乘以 10 的 3 次方将得到一个没有小数位的数字。模数% 1 == 0 时为整数。所以我想出了这个......

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

    n 小于(不等于)小数位数时返回真。

    【讨论】:

      【解决方案5】:

      基础是知道如何测试是否有小数位,这是通过将值与其四舍五入进行比较来完成的

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

      然后,要计算 3 位小数,您只需将数字乘以 1000 即可:

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

      【讨论】:

        【解决方案6】:

        到目前为止提出的所有解决方案都不可扩展......如果您永远不会检查除 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;
        }       
        

        【讨论】:

          【解决方案7】:

          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));
          

          【讨论】:

            【解决方案8】:

            基于@RodH257 解决方案的另一个选项,但作为扩展方法进行了重新设计:

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

            然后您可以将其称为:

            If !(tableA.Qty * tableA.Unit).HasThisManyDecimalPlacesOrLess(3)) return;
            

            【讨论】:

            • 我打算回答同样的问题,扩展方法肯定是最有用的解决方案。我将方法命名为 HasMaxDecimalPlaces() 只是为了将“这个多或少”简化为“最大”
            【解决方案9】:

            可能有一种更优雅的方法可以做到这一点,但我会尝试一下

            1. a = 乘以 1000
            2. b = 截断 a
            3. 如果 (b != a) 则丢失了额外的精度

            【讨论】:

              【解决方案10】:
                  bool CountDecimalPlaces(decimal input)
                  {
                      return input*1000.0 == (int) (input*1000);
                  }
              

              【讨论】:

                【解决方案11】:

                这是我的版本:

                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
                

                【讨论】:

                  【解决方案12】:

                  你能不能把它转换成一个字符串,然后做一个 len 函数,还是不能覆盖你的情况?

                  跟进问题: 300.4可以吗?

                  【讨论】:

                    【解决方案13】:
                    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
                    

                    【讨论】:

                    • 你能在你那里的代码中添加一些解决方案的解释吗?
                    • 我不关心额外的零,所以,decWork.ToString.Trim("0") 删除它们并且 .50 变为 .5 strDecAbs.SubstringstrDecAbs.Substring(strDecAbs.IndexOf(".") ).Length -1 给出从小数点开始的字符串长度,因为包含小数点,所以我减去 1。
                    • 除非指定文化信息,否则不适用于所有语言,所以要小心
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-09-20
                    • 2015-10-15
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-11-05
                    相关资源
                    最近更新 更多