C#检查小数是否超过3位小数?

我有一种情况我无法改变:一个数据库表(表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位小数,它可以适用于通用解决方案:

 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(对于trullygenerics函数,你需要类似“大整数”的东西)。

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

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

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

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

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

将一个带有3位小数的数字乘以10乘以3将得到一个没有小数位的数字。 当模数% 1 == 0时,它是整数。 所以我想出了这个……

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

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

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

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

可能有更优雅的方式来做到这一点,但我会尝试

  1. a =乘以1000
  2. b =截断a
  3. 如果(b!= a)那么还有额外的精度已经丢失
  bool CountDecimalPlaces(decimal input) { return input*1000.0 == (int) (input*1000); } 

你可以将它转换为字符串,只是做一个len函数或不会涵盖你的情况?

跟进问题:300.4可以吗?

 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 

还有一个基于@ RodH257解决方案的选项,但作为扩展方法重新编写:

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

然后你可以将其称为:

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