划分两个浮点数的安全方法?

时间:2014-07-17 08:20:32

标签: javascript node.js math floating-point ieee-754

划分两个IEEE 754浮点数的最安全的方法是什么?

在我的情况下,语言是JavaScript,但我想这并不重要。目标是避免正常的floating point pitfalls

我已经读过,可以使用"校正因子" (cf)(例如10提升到某个数字,例如10 ^ 10),如下所示:

(a * cf) / (b * cf)

但我不确定这会对分裂产生影响吗?

顺便说一下,我已经查看了Stack Overflow上的其他浮动帖子,而且我还没有找到关于如何划分两个浮点数的帖子。如果答案是在添加和分割时解决浮点问题的解决方案之间没有区别,那么请回答一下。

修改

我在评论中被问到我所指的哪些陷阱,所以我想我只是在这里添加一个快速注释,以及那些看不懂的人。注释:

当添加0.1和0.2时,你会期望得到0.3,但是使用浮点运算得到0.30000000000000004(至少在JavaScript中)。这只是常见陷阱的一个例子。

上面的问题在Stack Overflow上讨论了很多次,但是我不知道分割时会发生什么,以及它与添加或乘法时发现的陷阱有什么不同。可能没有风险,在这种情况下,这将是一个非常好的答案。

3 个答案:

答案 0 :(得分:6)

最安全的方法是简单地划分它们。任何预定标记都将无效,或增加舍入误差,或导致上溢或下溢。

如果您使用2的幂进行预分频,则可能会导致上溢或下溢,但结果不会产生任何差异。

如果您使用任何其他数字进行预缩放,则会在乘法上引入其他舍入步骤,这可能会导致除法结果的舍入误差增加。

如果你只是划分,结果将是两个输入的比率最接近的可表示数字。

IEEE 754 64位浮点数非常精确。可以表示几乎10 ^ 16中的一个部分的差异。

有一些操作,例如发言权和精确比较,甚至使极低重要性的比特变得重要。如果您一直在阅读浮点陷阱,您应该已经看过例子。避免那些。将输出舍入到适当的小数位数。小心添加非常不同的数量。

以下程序演示了使用10到10e20的每个10的幂作为比例因子的效果。大多数得到的结果与不乘以6.0相同,这也是有理数算术结果。有些人会得到稍大的结果。

您可以通过更改ab的初始值设定项来尝试不同的除法问题。在四舍五入到双倍之后,程序会打印出它们的确切值。

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    double mult = 10;
    double a = 2;
    double b = 1.0 / 3.0;
    System.out.println("a=" + new BigDecimal(a));
    System.out.println("b=" + new BigDecimal(b));
    System.out.println("No multiplier result="+(a/b));
    for (int i = 0; i < 20; i++) {
      System.out.println("mult="+mult + " result="+((a * mult) / (b * mult)));
      mult *= 10;
    }
  }
}

输出:

a=2
b=0.333333333333333314829616256247390992939472198486328125
No multiplier result=6.0
mult=10.0 result=6.000000000000001
mult=100.0 result=6.000000000000001
mult=1000.0 result=6.0
mult=10000.0 result=6.000000000000001
mult=100000.0 result=6.000000000000001
mult=1000000.0 result=6.0
mult=1.0E7 result=6.000000000000001
mult=1.0E8 result=6.0

答案 1 :(得分:5)

浮点除法将产生与加法或乘法运算完全相同的“陷阱”,并且没有任何预缩放量可以解决它 - 最终结果是最终结果,它是IEEE-754中的内部表示。导致“问题”。

解决方案是在计算过程中完全忘记这些精度问题,并尽可能晚地执行舍入,即仅在显示计算结果时,在数字处使用为此目的精确提供的.toFixed()函数将其转换为字符串。

答案 2 :(得分:0)

.tofixed()不是划分浮点数的好方法。 使用javascript尝试:4.11 / 100,你会感到惊讶。

4.11 / 100 = 0.041100000000000005

并非所有浏览器都能获得相同的结果。 正确的解决方案是将float转换为整数:

parseInt(4.11 * Math.pow(10, 10)) / (100 * Math.pow(10, 10)) = 0.0411