舍入双精度中的重要数字,而不是小数位

时间:2015-03-20 05:59:03

标签: c# double rounding

我需要对双打的有效数字进行舍入。例 回合(1.2E-20,0)应变为1.0E-20

我不能使用返回0的Math.Round(1.2E-20,0),因为Math.Round()不会在浮点数中舍入有效数字,而是舍入到十进制数字,即在E中加倍0

当然,我可以这样做:

double d = 1.29E-20;
d *= 1E+20;
d = Math.Round(d, 1);
d /= 1E+20;

哪个确实有效。但这并不是:

d = 1.29E-10;
d *= 1E+10;
d = Math.Round(d, 1);
d /= 1E+10;

在这种情况下,d为0.00000000013000000000000002。问题是双重存储内部分数为2,它不能完全匹配10的分数。在第一种情况下,似乎C#只处理*和/的指数,但在第二种情况下,它产生一个实际的*或/操作,然后导致问题。

当然,我需要一个总能给出正确结果的公式,而不仅仅是有时候。

意思是在舍入后我不应该使用任何双重操作,因为双算术不能精确处理小数部分。

上面计算的另一个问题是没有双函数返回double的指数。当然可以使用数学库来计算它,但可能很难保证这与双内部代码的结果完全相同。

在我的绝望中,我考虑将double转换为字符串,找到有效数字,进行舍入并将舍入后的数字转换回字符串然后最终将其转换为double。丑,对吧?在所有情况下也可能无法正常工作: - (

是否有任何图书馆或任何建议如何正确舍入双重有效数字?

PS:在声明这是一个重复的问题之前,请确保您理解了重要数字和小数位之间的区别

1 个答案:

答案 0 :(得分:3)

  

问题是双重存储内部分数为2,而不能完全匹配10的分数

这是一个问题,是的。如果在您的方案中很重要,则需要使用将数字存储为十进制而不是二进制的数字类型。在.NET中,该数字类型为decimal

请注意,对于许多计算任务(例如,不是货币),double类型很好。与使用double时存在的任何其他舍入错误相比,您没有完全您要查找的值的事实不再是一个问题。

另请注意,如果唯一的目的是显示号码,您甚至不需要自己进行舍入。您可以使用自定义数字格式来完成相同的操作。例如:

double value = 1.29e-10d;
Console.WriteLine(value.ToString("0.0E+0"));

这将显示字符串1.3E-10;

  

上面计算的另一个问题是没有双函数返回双重

的指数

我不确定你的意思。 Math.Log10()方法就是这样做的。当然,它返回给定数字的精确指数,基数为10.根据您的需要,您实际上更喜欢Math.Floor(Math.Log10(value)),它会为您提供将显示的指数值用科学记数法。

  

可能很难保证这与双内码完全相同

由于double的内部存储使用IEEE 二进制格式,其中指数和尾数都存储为二进制数,所以显示的指数基数10永远不会是"与双内码完全相同"无论如何。当然,指数是一个整数,可以准确表达。但它不是首先存储十进制值。

在任何情况下,Math.Log10()将始终返回有用的值。

  

是否有任何图书馆或任何建议如何正确舍入双重有效数字?

如果您只是为了展示而需要打圆,那么根本不做任何数学运算。只需使用自定义数字格式字符串(如上所述)以您希望的方式格式化值。

如果您确实需要自己进行舍入,那么根据您的描述,我认为以下方法应该有效:

static double RoundSignificant(double value, int digits)
{
    int log10 = (int)Math.Floor(Math.Log10(value));
    double exp = Math.Pow(10, log10);
    value /= exp;
    value = Math.Round(value, digits);
    value *= exp;

    return value;
}