双精度和peridoc小数

时间:2015-01-08 12:02:04

标签: c++ double lisp rational-number ieee

我正在研究lisp解释器并实现有理数。我认为他们有超过双打的优势,能够代表1/3的数字。我做了一些计算来比较结果。我对结果感到惊讶

有双打

(* 3.0 (/ 1.0 3.0)) -> 1
(* 3.0 (/ 4.0 3.0)) -> 4
(* 81.0 (/ 1.0 81.0)) -> 1

比例:

(* 3 (/ 1 3)) -> 1
(* 3 (/ 4 3)) -> 4
(* 81 (/ 1 81)) -> 1

为什么浮点运算的结果准确?一定要失去精确度。双打不能存储无限数量的数字。或者我会错过什么?

我使用小型C-Application进行了快速测试。结果相同。

#include <stdio.h>

int main()   
{  
    double a1 = 1, b1 = 3;  
    double a2 = 1, b2 = 81;  

    printf("result : %f\n", a1 / b1 * b1); 
    printf("result : %f\n", a2 / b2 * b2);

    return 0;  
}  

输出是:

结果:1​​.000000
结果:1.000000

MFG

马丁

1 个答案:

答案 0 :(得分:2)

对于第一种情况,乘法的确切结果是介于1.0和最大双精度之间的一半,小于1.0。根据IEEE 754舍入到最接近的规则,中间数字舍入为偶数,在本例中为1.0。实际上,乘法结果的舍入解除了舍入结果舍入引起的误差。

这个Java程序说明了正在发生的事情。对BigDecimal和BigDecimal算术运算的转换都是精确的:

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    double a1 = 1, b1 = 3;

    System.out.println("Final Result: " + ((a1 / b1) * b1));
    BigDecimal divResult = new BigDecimal(a1 / b1);
    System.out.println("Division Result: " + divResult);
    BigDecimal multiplyResult = divResult.multiply(BigDecimal.valueOf(3));
    System.out.println("Multiply Result: " + multiplyResult);
    System.out.println("Error rounding up to 1.0: "
        + BigDecimal.valueOf(1).subtract(multiplyResult));
    BigDecimal nextDown = new BigDecimal(Math.nextAfter(1.0, 0));
    System.out.println("Next double down from 1.0: " + nextDown);
    System.out.println("Error rounding down: "
        + multiplyResult.subtract(nextDown));
  }
}

输出结果为:

Final Result: 1.0
Division Result: 0.333333333333333314829616256247390992939472198486328125
Multiply Result: 0.999999999999999944488848768742172978818416595458984375
Error rounding up to 1.0: 5.5511151231257827021181583404541015625E-17
Next double down from 1.0: 0.99999999999999988897769753748434595763683319091796875
Error rounding down: 5.5511151231257827021181583404541015625E-17

第二个类似情况的输出是:

Final Result: 1.0
Division Result: 0.012345679012345678327022824305458925664424896240234375
Multiply Result: 0.9999999999999999444888487687421729788184165954589843750
Error rounding up to 1.0: 5.55111512312578270211815834045410156250E-17
Next double down from 1.0: 0.99999999999999988897769753748434595763683319091796875
Error rounding down: 5.55111512312578270211815834045410156250E-17

该程序说明了可以累积舍入误差的情况:

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    double tenth = 0.1;
    double sum = 0;
    for (int i = 0; i < 10; i++) {
      sum += tenth;
    }
    System.out.println("Sum: " + new BigDecimal(sum));
    System.out.println("Product: " + new BigDecimal(10.0 * tenth));
  }
}

输出:

Sum: 0.99999999999999988897769753748434595763683319091796875
Product: 1

乘以10轮到1.0。通过重复添加进行相同的乘法并不能得到确切的答案。