将有效数字四舍五入,而不是小数点后一位 [英] Rounding the SIGNIFICANT digits in a double, not to decimal places

查看:169
本文介绍了将有效数字四舍五入,而不是小数点后一位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要将有效数字加倍。示例
Round(1.2E-20,0)应该变成1.0E-20

I need to round significant digits of doubles. Example Round(1.2E-20, 0) should become 1.0E-20

我不能使用Math.Round(1.2E-20,0),它返回0,因为Math.Round()不会将有效数字四舍五入为整数,而是四舍五入为十进制数字,即E为0时加倍。

I cannot use Math.Round(1.2E-20, 0), which returns 0, because Math.Round() doesn't round significant digits in a float, but to decimal digits, i.e. doubles where E is 0.

当然,我可以做这样的事情:

Of course, I could do something like this:

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

实际上有效。但这不是:

Which actually works. But this doesn't:

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

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

In this case, d is 0.00000000013000000000000002. The problem is that double stores internally fractions of 2, which cannot match exactly fractions of 10. In the first case, it seems C# is dealing just with the exponent for the * and /, but in the second case it makes an actual * or / operation, which then leads to problems.

当然,我需要一个始终能给出正确结果的公式,不仅在某些时候。

Of course I need a formula which always gives the proper result, not only sometimes.

意思是我不能在四舍五入后使用任何double操作,因为double算术不能精确地处理小数。

Meaning I should not use any double operation after the rounding, because double arithmetic cannot deal exactly with decimal fractions.

上述计算的另一个问题是没有双精度函数返回双精度指数。当然,可以使用数学库进行计算,但是可能很难保证它始终具有与双重内部代码相同的结果。

Another problem with the calculation above is that there is no double function returning the exponent of a double. Of course one could use the Math library to calculate it, but it might be difficult to guarantee that this has always precisely the same result as the double internal code.

无奈之下,我考虑了将双精度型转换为字符串,找到有效数字,进行四舍五入,然后将四舍五入的数字转换回字符串,然后最终将其转换为双精度型。丑陋吧?可能在所有情况下也可能无法正常工作:-(

In my desperation, I considered to convert a double to a string, find the significant digits, do the rounding and convert the rounded number back into a string and then finally convert that one to a double. Ugly, right ? Might also not work properly in all case :-(

是否有任何库或建议如何正确舍入双精度数的有效数字?

Is there any library or any suggestion how to round the significant digits of a double properly ?

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

PS: Before declaring that this is a duplicate question, please make sure that you understand the difference between SIGNIFICANT digits and decimal places

推荐答案


问题是double在内部存储2的分数,而分数不能精确匹配10的分数

The problem is that double stores internally fractions of 2, which cannot match exactly fractions of 10

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

That is a problem, yes. If it matters in your scenario, you need to use a numeric type that stores numbers as decimal, not binary. In .NET, that numeric type is decimal.

请注意,对于许多计算任务(例如,不包括货币),是double 类型很好,因为您没有得到准确的 值,这与存在的其他四舍五入错误无关。 n使用 double

Note that for many computational tasks (but not currency, for example), the double type is fine. The fact that you don't get exactly the value you are looking for is no more of a problem than any of the other rounding error that exists when using double.

还要注意,如果唯一的目的是显示 数字,您甚至不需要自己四舍五入。您可以使用自定义数字格式来完成此操作。例如:

Note also that if the only purpose is for displaying the number, you don't even need to do the rounding yourself. You can use a custom numeric format to accomplish the same. For example:

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

将显示字符串 1.3E-10 ;


上述计算的另一个问题是,没有双精度函数返回双精度指数

Another problem with the calculation above is that there is no double function returning the exponent of a double

我不确定您的意思。 Math.Log10()方法可以做到这一点。当然,它会返回给定数字以10为底的 exact 指数。根据您的需要,您实际上更喜欢 Math.Floor(Math.Log10(value)),它会为您提供以科学计数法显示的指数值。

I'm not sure what you mean here. The Math.Log10() method does exactly that. Of course, it returns the exact exponent of a given number, base 10. For your needs, you'd actually prefer Math.Floor(Math.Log10(value)), which gives you the exponent value that would be displayed in scientific notation.


可能很难保证由于内部存储了 double,因此,它始终与double内部代码完全相同

it might be difficult to guarantee that this has always precisely the same result as the double internal code

code>使用IEEE binary 格式,其中指数和尾数均存储为二进制数,所显示的指数基数10永远不会与双内部代码完全相同。当然,指数可以是整数。但这不像是将十进制值存储在首位。

Since the internal storage of a double uses an IEEE binary format, where the exponent and mantissa are both stored as binary numbers, the displayed exponent base 10 is never "precisely the same as the double internal code" anyway. Granted, the exponent, being an integer, can be expressed exactly. But it's not like a decimal value is being stored in the first place.

无论如何, Math.Log10()将始终返回有用的值。

In any case, Math.Log10() will always return a useful value.


是否存在任何库或建议如何正确舍入双精度位的有效数字? / p>

Is there any library or any suggestion how to round the significant digits of a double properly ?

如果只需要四舍五入即可显示,则无需进行任何数学运算。只需使用自定义数字格式字符串(如上所述)即可按所需方式设置值的格式。

If you only need to round for the purpose of display, don't do any math at all. Just use a custom numeric format string (as I described above) to format the value the way you want.

如果您实际上需要自行舍入,那么我认为根据您的描述,以下方法应该可以工作:

If you actually need to do the rounding yourself, then I think the following method should work given your description:

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

这篇关于将有效数字四舍五入,而不是小数点后一位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆