将double截断为x位有效数字
本文关键字:有效数字 double | 更新日期: 2023-09-27 18:04:20
我一直在愉快地使用截断扩展方法,正如@P Daddy在这里发布的:https://stackoverflow.com/a/374470/4123190
static double TruncateToSignificantDigits(this double d, int digits){
if(d == 0)
return 0;
double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1 - digits);
return scale * Math.Truncate(d / scale);
}
这主要工作如预期,但我遇到了一个情况,它失败了,99999999999999999到3位数字给出990000000000000。这是一个显示失败的提示:https://dotnetfiddle.net/4tN7nv
对于我的特定用例,我需要使用double,并且需要使用它们提供的全部范围。虽然失败的情况看起来是由一些奇怪的浮点数引起的,但显然可以将数字转换为截断的版本,因为将其转换为字符串,将其截断,然后将其转换回双精度类型将保持预期的结果。
如何改进以适用于所有double ?
OK。我发现这是一个关于各种数学函数的准确性的问题,所以我用了一堆复杂的方法来解决这个问题,当我意识到我们可以通过使用字符串输入而不是双输入来规避这些问题。输出仍然是双精度的,只是输入被改成了字符串。因此,您可以使用value.ToString()调用该函数。
注意,这只会产生最多9位数字的精度。我想这已经足够了因为你说的是截断到三位数。如果你需要九个以上的数字,请告诉我,我会重新开始(这是可能的)。
下面是修改后的方法:public void testTruncate()
{
for (int i = 3; i < 1000; i++)
{
string testNumber = "";
if (Math.Log10(Math.Pow(10, i)) > 9)
{
testNumber = (Math.Pow(10, i) / Math.Pow(10, i - 9) - 1).ToString();
for (int d = 0; d < Math.Log10(Math.Pow(10, i)) - testNumber.Length; d++)
{
testNumber += "0";
}
if (double.Parse(testNumber) > double.MaxValue)
{
break;
}
}
else
{
testNumber = (Math.Pow(10, i) - 1).ToString();
}
Console.WriteLine(testNumber+" truncated to "+ doTruncate(testNumber, 3, true).ToString("N0")+"'n");
}
}
public double doTruncate(string bigNumString, double numberOfDigitsToTruncateTo, bool addZeroesBack)
{
if (bigNumString.Length <= numberOfDigitsToTruncateTo)
{
return double.Parse(bigNumString);
}
string ret = bigNumString.Substring(0,(int)numberOfDigitsToTruncateTo);
if (addZeroesBack)
{
for (int i = 0; i < bigNumString.Length - numberOfDigitsToTruncateTo; i++)
{
ret += "0";
}
}
double answer = double.Parse(ret);
if (answer > double.MaxValue)
{
return -1;//value too large
}
return answer;
}
,下面是testTruncate从999到double极限的结果。MaxValue
999截断为999
9999截断为9990
99999截断为99900
999999截断为999,000
9999999截断为9990000
99999999截断为99900,000
999999999截断为999,000,000
9999999990截断为9990,000,000
9999999990截断为9990,000,000
99999999900截断为99900,000,000
99999999900截断为99900,000,000
999999999000截断为999,000,000,000
999999999000截断为999,000,000,000
9999999990000截断为9990,000,000,000
9999999990000截断为9990,000,000,000
99999999900000截断为99900,000,000,000
99999999900000截断为99900,000,000,000
999999999000000截断为999,000,000,000,000
999999999000000截断为999,000,000,000,000
9999999990000000截断为9990,000,000,000,000
9999999990000000截断为9990,000,000,000,000
99999999900000000截断为99900,000,000,000,000
99999999900000000截断为99900,000,000,000,000
999999999000000000截断为999,000,000,000,000,000
999999999000000000截断为999,000,000,000,000,000
9999999990000000000截断为9990,000,000,000,000,000
9999999990000000000截断为9990,000,000,000,000,000
99999999900000000000截断为99900,000,000,000,000,000
99999999900000000000截断为99900,000,000,000,000,000
999999999000000000000截断为999,000,000,000,000,000,000,000
999999999000000000000截断为999,000,000,000,000,000,000,000
9999999990000000000000截断为9990,000,000,000,000,000,000,000
9999999990000000000000截断为9990,000,000,000,000,000,000,000
99999999900000000000000截断为99900,000,000,000,000,000,000
99999999900000000000000截断为99900,000,000,000,000,000,000
…以此类推……999999999000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000截到999000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
恕我直言,您根本不应该这样做,因为扁平点数表示的性质。如果你需要四舍五入只是为了在UI中显示,那么你有很好的内置ToString函数,它提供了精度特性。如果需要对两个数字进行近似比较,可以将两个数字归一化,并在有效位域中取整数