浮点数操作精度损失

众所周知,浮点数操作是有精度损失的。这里对精度损失的原因进行跟踪。

废话少说,先上代码:

import java.math.BigDecimal;
public class Main {
    static void parseFloatDetails(float v) {
        int i = Float.floatToRawIntBits(v);
        int sign = ((i & 0x80000000) >> 31); // 浮点数的正负符号
        int exponent = ((i & 0x7f800000) >> 23); // 浮点数指数部分
        exponent -= 127;
        int significand = (i & 0x007fffff); // 浮点数尾数部分
        String significand_str = Integer.toBinaryString(significand);
        while (significand_str.length() < 23)
            significand_str = "0" + significand_str;
        // value = (1 + significand / 2^23) / 2^exponent
        BigDecimal part1 = BigDecimal.valueOf(1.0).add(
            BigDecimal.valueOf(significand).divide(
            BigDecimal.valueOf(2.0).pow(23)));
        BigDecimal part2 = BigDecimal.valueOf(2.0).pow(Math.abs(exponent));
        part1 = (exponent >= 0 ? part1.multiply(part2) : part1.divide(part2));
        System.out.println("1." + significand_str + "B * 2^" + exponent + " = " + part1.floatValue()
            + " (" + v + ")");
    }
    public static void main(String[] args) throws Throwable {
        parseFloatDetails(1.3f);
        parseFloatDetails(1.0f);
        parseFloatDetails(1.3f - 1.0f);
        parseFloatDetails(0.3f);
    }
}
/* stdout:
1.01001100110011001100110B * 2^0 = 1.3 (1.3)
1.00000000000000000000000B * 2^0 = 1.0 (1.0)
1.00110011001100110011000B * 2^-2 = 0.29999995 (0.29999995)
1.00110011001100110011010B * 2^-2 = 0.3 (0.3)
*/

这里利用了浮点数 IEEE二进制浮点数算术标准IEEE 754 的相关参数。


精度损失的原因

1) 部分十进制浮点数用二进制存储存在精度损失。

例如 十进制0.3D, 二进制无法精确表示,对应二进制 0.01001100110011001....B (无限循环二进制小数)

2) 所有的二进制浮点数都可无损的转换为十进制浮点数,只要不限制小数的尾数。

因为, 2^-1, 2^-2, 2^-3 .... 都可转换为十进制有限小数

3) 浮点数的四则运算都存在着精度损失。

哪怕是数量级相近的加减法(例如 1.3 - 1.0)。因为若 A和B运算生成C,由于二进制的关系,ABC的指数部分必定不会全部相等,则计算过程中必定存在浮点的指数对齐过程,同时伴随着尾数的位移和补零,而被舍弃的无限循环部分没有参与运算,则存在精度损失。(参见上面程序示例)

(如果要跟踪其他运算符精度损失详情或者双精度的浮点数计算,只需小小改动上述程序即可)

4) 浮点数进行等号比较的问题:

1.3 - 1.0 == 0.3 的比较方式必然导致失败。


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值