[英]C# Check if a decimal has more than 3 decimal places?
I have a situation that I cannot change: one database table (table A) accepts 6 decimal places, while a related column in a different table (table B) only has 3 decimal places.我有一种无法更改的情况:一个数据库表(表 A)接受 6 个小数位,而另一个表(表 B)中的相关列只有 3 个小数位。
I need to copy from A to B, but if A has more than 3 decimal places the extra data will be lost.我需要从 A 复制到 B,但如果 A 的小数位超过 3 位,额外的数据将丢失。 I cant change the table definition but I can add a workaround.
我无法更改表定义,但可以添加解决方法。 So I'm trying to find out how to check if a decimal has more than 3 decimal places or not?
所以我试图找出如何检查小数点是否超过 3 个小数位?
eg例如
Table A
Id, Qty, Unit(=6dp)
1, 1, 0.00025
2, 4000, 0.00025
Table B
Id, TotalQty(=3dp)
I want to be able to find out if Qty * Unit from Table A has more than 3 decimals (row 1 would fail, row 2 would pass):我希望能够找出表 A 中的 Qty * Unit 是否有超过 3 位小数(第 1 行会失败,第 2 行会通过):
if (CountDecimalPlaces(tableA.Qty * tableA.Unit) > 3)
{
return false;
}
tableB.TotalQty = tableA.Qty * tableA.Unit;
How would I implement the CountDecimalPlaces(decimal value) {}
function?我将如何实现
CountDecimalPlaces(decimal value) {}
函数?
You could compare the value of the number rounded to 3 decimal places with the original value.您可以将四舍五入到小数点后 3 位的数字的值与原始值进行比较。
if (Decimal.Round(valueDecimal, 3) != valueDecimal)
{
//Too many decimals
}
This works for 3 decimal places, and it can be adapted for a generic solution:这适用于 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));
}
For a real generic solution, you'll need to "deconstruct" the decimal value in its parts - take a look at Decimal.GetBits for more information.对于真正的通用解决方案,您需要在其部分“解构”十进制值 - 查看Decimal.GetBits了解更多信息。
Update: this is a simple implementation of a generic solution which works for all decimals whose integer part is less than long.MaxValue (you'd need something like a "big integer" for a trully generic function).更新:这是通用解决方案的简单实现,适用于整数部分小于 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));
}
This is a very simple one line code to get count of decimals in a Decimal:这是一个非常简单的一行代码,用于获取 Decimal 中的小数位数:
decimal myDecimal = 1.000000021300010000001m;
byte decimals = (byte)((Decimal.GetBits(myDecimal)[3] >> 16) & 0x7F);
Multiplying a number with 3 decimal places by 10 to the power of 3 will give you a number with no decimal places.将一个有 3 个小数位的数字乘以 10 的 3 次方将得到一个没有小数位的数字。 It's a whole number when the modulus
% 1 == 0
.当模数
% 1 == 0
时,它是一个整数。 So I came up with this...所以我想出了这个...
bool hasMoreThanNDecimals(decimal d, int n)
{
return !(d * (decimal)Math.Pow(10, n) % 1 == 0);
}
Returns true when n
is less than (not equal) to the number of decimal places.当
n
小于(不等于)小数位数时返回 true。
The basics is to know how to test if there are decimal places, this is done by comparing the value to its rounding基础知识是知道如何测试是否有小数位,这是通过将值与其舍入进行比较来完成的
double number;
bool hasDecimals = number == (int) number;
Then, to count 3 decimal places, you just need to do the same for your number multiplied by 1000:然后,要计算 3 个小数位,您只需要对乘以 1000 的数字执行相同的操作:
bool hasMoreThan3decimals = number*1000 != (int) (number * 1000)
All of the solutions proposed so far are not extensible ... fine if you are never going to check a value other than 3, but I prefer this because if the requirement changes the code to handle it is already written.到目前为止提出的所有解决方案都是不可扩展的......如果你永远不会检查 3 以外的值,那很好,但我更喜欢这个,因为如果需求改变了代码来处理它已经写好了。 Also this solution wont overflow.
此解决方案也不会溢出。
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 solution will need to check for 0 otherwise "while ((lowDecimal % 10) == 0)" will produce an infinity loop when called with dec = 0 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));
One more option based on @RodH257's solution, but reworked as an extension method:另一个基于@RodH257 解决方案的选项,但作为扩展方法重新设计:
public static bool HasThisManyDecimalPlacesOrLess(this decimal value, int noDecimalPlaces)
{
return (Decimal.Round(value, noDecimalPlaces) == value);
}
You can then call that as:然后,您可以将其称为:
If !(tableA.Qty * tableA.Unit).HasThisManyDecimalPlacesOrLess(3)) return;
bool CountDecimalPlaces(decimal input)
{
return input*1000.0 == (int) (input*1000);
}
There is probably a more elegant way to do this, but off the top of my head I would try可能有一种更优雅的方法来做到这一点,但我会尝试
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
could you convert it to a string and just do a len function or would that not cover your situation?你能把它转换成一个字符串然后只做一个 len 函数还是不能涵盖你的情况?
follow up question: would 300.4 be ok?后续问题:300.4 可以吗?
Here is my version:这是我的版本:
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;
}
I tried first @carlosfigueira/@Henrik Stenbæk
, but their version does not work with 324000.00m
我首先尝试了
@carlosfigueira/@Henrik Stenbæk
,但他们的版本不适用于324000.00m
TEST:测试:
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.