C# 返回小数位数的自定义方法显示奇怪的行为
我正在写一个简单的方法来计算一个十进制值的小数位数。方法如下所示:C# 返回小数位数的自定义方法显示奇怪的行为,c#,methods,decimal,C#,Methods,Decimal,我正在写一个简单的方法来计算一个十进制值的小数位数。方法如下所示: public int GetDecimalPlaces(decimal decimalNumber) { try { int decimalPlaces = 1; double powers = 10.0; if (decimalNumber > 0.0m) { while (((double)decimalNumber * powers) % 1 != 0.0) {
public int GetDecimalPlaces(decimal decimalNumber) {
try {
int decimalPlaces = 1;
double powers = 10.0;
if (decimalNumber > 0.0m) {
while (((double)decimalNumber * powers) % 1 != 0.0) {
powers *= 10.0;
++decimalPlaces;
}
}
return decimalPlaces;
我已经针对一些测试值运行了它,以确保一切正常,但我在上一个测试中发现了一些非常奇怪的行为:
int test = GetDecimalPlaces(0.1m);
int test2 = GetDecimalPlaces(0.01m);
int test3 = GetDecimalPlaces(0.001m);
int test4 = GetDecimalPlaces(0.0000000001m);
int test5 = GetDecimalPlaces(0.00000000010000000001m);
int test6 = GetDecimalPlaces(0.0000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001m);
测试1-5工作正常,但测试6返回23。我知道传入的值超过了最大十进制精度,但为什么是23?另一件我觉得奇怪的事情是,当我在test6调用之后在GetDecimalPlaces方法中放置断点时,该方法中的decimalNumber值与test5中的值相同(20位小数),尽管传入的值有20位小数,但返回的是23位小数
也许这只是因为我传递的数字有太多的小数位,事情变得不稳定,但我想确保我没有遗漏一些根本性的错误,这些错误可能会在以后的过程中影响其他值的计算。您实际测试的数字是:
0.0000000001000000000100000000
这是最接近0.00000000010000000100000000000001000000000000010000000000000100000000000001000000000000010000000000000100000000000001000000000000010000000000000100000000000001的精确十进制值
所以正确答案实际上是20。然而,您的代码给出23,因为您使用的是二进制浮点算法,没有明显的原因。这将在你的计算中引入错误,完全是不必要的。如果更改为一致使用十进制,则可以:
public static int GetDecimalPlaces(decimal decimalNumber) {
int decimalPlaces = 1;
decimal powers = 10.0m;
if (decimalNumber > 0.0m) {
while ((decimalNumber * powers) % 1 != 0.0m) {
powers *= 10.0m;
++decimalPlaces;
}
}
return decimalPlaces;
}
(建议)你可以这样计算:
public static int GetDecimalPlaces(decimal decimalNumber)
{
var s = decimalNumber.ToString();
return s.Substring(s.IndexOf(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) + 1).Length;
}
还有另一种方法可以做到这一点,而且可能工作得更快,因为只有当十进制数有“尾随零”问题时,它才使用余数运算 基本思想: 在.NET中,任何十进制数都以以下形式存储在内存中 m*数学幂(10,-p) 其中m是尾数(96位大小),p是顺序(值从0到28) 方法从decimal结构中检索此表示形式,并将其作为int数组(长度为4)返回 利用这些数据,我们可以构造另一个十进制数。如果我们只使用尾数,而不使用“Math.Power(10,-p)”部分,结果将是整数小数。如果这个整数可以被10整除,那么我们的源数有一个或多个尾随零 这是我的代码
static int GetDecimalPlaces(decimal value)
{
// getting raw decimal structure
var raw = decimal.GetBits(value);
// getting current decimal point position
int decimalPoint = (raw[3] >> 16) & 0xFF;
// using raw data to create integral decimal with the same mantissa
// (note: it always will be absolute value because I do not analyze
// the sign information of source number)
decimal integral = new decimal(raw[0], raw[1], raw[2], false, 0);
// disposing from trailing zeros
while (integral > 0 && integral % 10 == 0)
{
decimalPoint--;
integral /= 10;
}
// returning the answer
return decimalPoint;
}
只是一个猜测,但我知道,如果运行代码的硬件支持它,或者在将其存储到变量之前可以对编译时文本执行计算,那么该语言保留使用比它所说的更高精度的权利;给定的精度是最小值,而不是精确值。出于好奇,将其转换为字符串并计算小数点后的字符不是更快更容易吗?我知道像23.23e3这样的数字会有一些问题,但适应它应该不难。@TimSchmelter哈哈哈谢谢你让我度过了美好的一天hilarious@TyCobb我最初是使用string.Split('.')实现的计算数字,但问题出现在不使用“.”作为小数分隔符的其他CultureInfo中,然后必须开始对字符串进行各种杂乱的处理,以确保值仍然正确。这要精确得多,确保我们只处理数值而不是字符串。请注意为什么20是正确答案;这是因为后面的零被忽略了。该函数返回的结果永远不会超过28(29?),这是C#十进制中有效位数的最大值。要想得到更多,我想你需要使用字符串表示法。非常感谢你澄清了这一点。我可以看到问题的根源,双打肯定干扰了我的价值观。不,请阅读我在原始帖子上的评论,看看为什么使用字符串是不可接受的。我明白了。但是,如果您在应用程序级别设置区域性,也许您可以使用我发布的编辑版本。