如何正确地舍入一个非常小的负数?

时间:2013-06-12 00:39:27

标签: floating-point rounding

我们从之前的计算中获得了一个非常小的负数,例如-0.123e-35。 该数字可用于其他一些公式中。是否正确的方法将这个数字舍入到十个小数位为零'0'? 如果这个数字仅用于加法运算,但在乘法运算中会产生错误的结果,这是可以的。 如何正确地做到这一点?

UPD

为什么需要我?我在矩阵上实现了DCT方法。在测试矩阵8x8上填充'1'。我明白了:

8 -7.49778027488703e-13 7.64321008887262e-13 -7.90698562193149e-13 8.26182651759402e-13 -8.78937758371177e-13 9.51152198392764e-13 -1.04158952401295e-12
-7.51721016906324e-13 1.66533453693773e-16 -2.77555756156289e-17 2.77555756156289e-17 2.77555756156289e-17 2.77555756156289e-17 -2.77555756156289e-17 1.38777878078145e-17
7.65773344411544e-13 2.77555756156289e-17 -1.38777878078145e-16 -2.77555756156289e-17 0 0 0 -1.38777878078145e-17
-7.91522860733958e-13 0 5.55111512312578e-17 -5.55111512312578e-17 -2.77555756156289e-17 -2.77555756156289e-17 -1.38777878078145e-17 -1.38777878078145e-17
8.26025642513534e-13 8.32667268468867e-17 -2.77555756156289e-17 0 0 5.55111512312578e-17 -6.93889390390723e-17 -6.93889390390723e-18
-8.78466730633572e-13 8.32667268468867e-17 -2.77555756156289e-17 -4.16333634234434e-17 1.11022302462516e-16 4.16333634234434e-17 -1.38777878078145e-17 5.55111512312578e-17
9.50946123757562e-13 -1.66533453693773e-16 -9.71445146547012e-17 2.77555756156289e-17 -9.71445146547012e-17 -1.38777878078145e-17 1.38777878078145e-17 -1.73472347597681e-17
-1.04131475783268e-12 -8.32667268468867e-17 4.16333634234434e-17 -4.16333634234434e-17 -1.38777878078145e-17 5.55111512312578e-17 2.77555756156289e-17 -4.16333634234434e-17

我也做同样的事情,但在Scilab中,它给了我这个:

8.    0.    0.    0.    0.    0.    0.    0.  
0.    0.    0.    0.    0.    0.    0.    0.  
0.    0.    0.    0.    0.    0.    0.    0.  
0.    0.    0.    0.    0.    0.    0.    0.  
0.    0.    0.    0.    0.    0.    0.    0.  
0.    0.    0.    0.    0.    0.    0.    0.  
0.    0.    0.    0.    0.    0.    0.    0.  
0.    0.    0.    0.    0.    0.    0.    0.

如你所见,只有零。而且我想理解我可以将这些非常小的负数舍入到零或不是吗?

5 个答案:

答案 0 :(得分:3)

这里有很多答案,建议你按照(x * 10^n) / 10^n的方式做一些事情。但是,通常情况下,舍入到n小数位。

二进制浮点不能精确地表示大多数有限的十进制值,例如0.1实际上是0.100000001490116 ... * 因此,如果您的目标是获得有限十进制扩展的精确表示(对于后续计算),这种方法不起作用;它只会为您提供最接近的可表示值。要获得精确的表示,您需要调查BigDecimal库/类(其详细信息因语言而异)。

但是,如果您只想显示值到10位小数,那么您的语言可能会提供与C printf("%.10f\n", x);类似的内容。为了显示目的,这实际上将舍入到10个小数位。

<小时/> *有关此重要问题的初学者介绍,请参阅http://floating-point-gui.de/

答案 1 :(得分:1)

要在JavaScript中舍入到10个小数位(因为问题最初被标记),只需执行以下操作。

var num = 0.0000000000000000000000000000000000123; // 1.23e-35
num = Math.round(num * 10000000000) / 10000000000;  // 0

var num = 0.000000000123; // 1.23e-10
num = Math.round(num * 10000000000) / 10000000000;  // 1e-10

请注意,虽然上述结果会产生您正在寻找的结果,但浮点值不是真正的十进制值,并且在某些情况下尝试“舍入”它们可能会产生意外结果。但是,此解决方案将确保将小于0.00000000005的任何数字舍入为0,我认为这正是您要寻找的。

对于您的问题,可能比舍入所有数字更好的解决方案将有条件地将值设置为0,如果它是&lt; 0.00000000005

if (num < 0.00000000005) num = 0;

答案 2 :(得分:1)

您所显示的信息并不能证明任何需要舍入为零的理由。最有可能的是,您应该忽略这样一个事实,即某些结果很小并继续正常工作。

完美的测试数据不是DCT的良好示范。实际数据很少会产生具有正好零值的DCT。此外,如果将DCT应用于实际数据并获得较小的值,则可能很小,因为这是数学上正确的结果,或者因为数学上正确的结果将为零但是存在一些错误。这意味着您无法确定DCT结果中的较小值是应该更正为零还是单独保留。因此,尝试将值更改为零实际上可能会使数据变得更糟。

此外,小值不太可能影响工作。您的应用程序将使用DCT的结果来修改或分析信号,然后继续执行逆变换或以其他方式使用结果。通常,这种进一步的使用不会受到数据中微小错误的显着影响。 (麦克风或其他传感器的误差或环境噪声可能比这些误差大。)

换句话说,这些小小的价值只会困扰你,人类,而不是计算机。忽略它们。

答案 3 :(得分:0)

在python中这是

round(n*(1e10))/(1e10)

答案 4 :(得分:0)

舍入到N个小数位的最简单方法是使用round,请注意这将返回long,因此您必须除以double值,否则可以得到整数除法。

double rounded = Math.round(d * 1e10) / 1e10;

使用1eN表示法可以更清楚地了解您想要舍入的数字。为确保不溢出,您需要进行边界检查。

public static double round10(double d) {
    final double factor = 1e10;
    return d > Long.MAX_VALUE / factor || d < -Long.MAX_VALUE / factor ?
            (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}

这里我使用的是铸造而不是圆角,因为它几乎同样准确但更快。