第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定

10年Java老司機告訴你:為什么永遠不要相信浮點數(shù)相等

標簽:
Java JavaScript

10年Java老司机告诉你:为什么永远不要相信浮点数相等

那个让我怀疑人生的线上Bug

还记得2019年的那个深夜,我正准备关电脑下班,突然钉钉疯狂响起:"财务系统账目不平,差了几分钱!"

作为一个工作5年的"老"程序员,我当时心想:这能有多大问题?肯定是业务逻辑哪里算错了。

结果查到最后,真相让我彻底震惊…

踩坑瞬间:0.1 + 0.2 ≠ 0.3

问题出现在一个看似简单的金额计算:

// 看起来完全没问题的代码
public class OrderCalculator {
    public boolean isAmountMatched(double expected, double actual) {
        return expected == actual; // 这就是罪魁祸首!
    }
  
    public static void main(String[] args) {
        double price1 = 0.1;
        double price2 = 0.2;
        double total = price1 + price2;
      
        System.out.println(total); // 输出:0.30000000000000004
        System.out.println(total == 0.3); // 输出:false
    }
}

我当时的内心独白:
"什么鬼?0.1 + 0.2 怎么可能不等于0.3?这不是小学数学吗?"

深挖真相:IEEE 754的"美丽陷阱"

通宵达旦研究后,我才明白这背后的原理。浮点数在计算机中采用IEEE 754标准存储:

十进制0.1的二进制表示:

0.1 = 0.0001100110011001100110011001100110011... (无限循环)

由于计算机只能存储有限位数,必须进行舍入,这就产生了精度误差!

// 让人绝望的真相
public void showFloatingPointTruth() {
    System.out.println("0.1的实际存储值:" + new BigDecimal(0.1));
    // 输出:0.1000000000000000055511151231257827021181583404541015625
  
    System.out.println("0.2的实际存储值:" + new BigDecimal(0.2));
    // 输出:0.200000000000000011102230246251565404236316680908203125
}

这下我懂了:计算机根本就没有准确存储0.1和0.2!

生产环境的血泪史

这个问题在金融系统中简直是灾难。我见过的真实案例:

案例1:积分计算错误

// 用户充值100元,按0.1的比例返积分
double rechargeAmount = 100.0;
double ratio = 0.1;
double points = rechargeAmount * ratio; // 实际是9.999999999999998

if (points >= 10.0) {
    // 用户应该得到10积分,结果什么都没有...
    giveUserPoints((int)points);
}

案例2:价格比较Bug

// 商品打折后价格对比
double originalPrice = 299.9;
double discountPrice = originalPrice * 0.8; // 239.91999999999996
double expectedPrice = 239.92;

if (discountPrice == expectedPrice) {
    // 永远不会执行,用户拿不到优惠
    applayDiscount();
}

解决方案1:BigDecimal - 精确计算的救星

痛定思痛,我开始用BigDecimal重构所有金额相关代码:

public class SafeMoneyCalculator {
  
    public static BigDecimal add(double a, double b) {
        return BigDecimal.valueOf(a).add(BigDecimal.valueOf(b));
    }
  
    public static boolean equals(BigDecimal a, BigDecimal b) {
        return a.compareTo(b) == 0; // 永远不要用equals!
    }
  
    // 实际项目中的应用
    public BigDecimal calculateOrderTotal(List<OrderItem> items) {
        return items.stream()
                   .map(item -> BigDecimal.valueOf(item.getPrice())
                                        .multiply(BigDecimal.valueOf(item.getQuantity())))
                   .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

解决方案2:容差比较 - 优雅的妥协

有些场景下,完全精确没必要,容差比较更实用:

public class FloatComparator {
    private static final double EPSILON = 1e-9; // 容差值
  
    public static boolean equals(double a, double b) {
        return Math.abs(a - b) < EPSILON;
    }
  
    public static boolean equals(double a, double b, double precision) {
        return Math.abs(a - b) < precision;
    }
  
    // 实际应用:GPS坐标比较
    public boolean isLocationMatch(double lat1, double lon1, double lat2, double lon2) {
        return equals(lat1, lat2, 1e-6) && equals(lon1, lon2, 1e-6);
    }
}

经验启示:不同场景的最佳实践

经过多年踩坑,我总结了一套实用指南:

场景分类表:

应用场景 推荐方案 理由
金融计算 BigDecimal 绝对精确,法律要求
科学计算 容差比较 允许误差,性能优先
游戏开发 float + 容差 性能要求极高
配置比较 String转换 避免精度问题

踩坑血泪:那些年我犯过的错

错误1:BigDecimal的equals陷阱

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1.00");
System.out.println(a.equals(b)); // false!scale不同

// 正确做法
System.out.println(a.compareTo(b) == 0); // true

错误2:字符串构造vs数值构造

// 错误:仍然有精度问题
BigDecimal wrong = new BigDecimal(0.1);

// 正确:字符串构造保证精确
BigDecimal right = new BigDecimal("0.1");
// 或者
BigDecimal right2 = BigDecimal.valueOf(0.1);

实战武器:我的工具类进化史

从最初的naive实现到现在的production-ready版本:

public class MoneyUtils {
    private static final int DEFAULT_SCALE = 2;
    private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_UP;
  
    public static BigDecimal createMoney(double amount) {
        return BigDecimal.valueOf(amount).setScale(DEFAULT_SCALE, DEFAULT_ROUNDING);
    }
  
    public static boolean isEqual(BigDecimal a, BigDecimal b) {
        if (a == null || b == null) return false;
        return a.compareTo(b) == 0;
    }
  
    public static boolean isGreater(BigDecimal a, BigDecimal b) {
        return a.compareTo(b) > 0;
    }
  
    // 安全的除法,避免无限小数
    public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor) {
        return dividend.divide(divisor, DEFAULT_SCALE, DEFAULT_ROUNDING);
    }
}

总结:10年经验的精华提炼

作为一个在浮点数坑里摸爬滚打10年的老司机,我的核心建议:

金科玉律:

  1. 永远不要用 == 比较浮点数
  2. 金融系统必须用BigDecimal
  3. 容差比较要选择合适的精度
  4. 字符串构造BigDecimal,避免二次精度损失

性能vs精度的权衡:

  • 追求绝对精确:BigDecimal
  • 性能优先:double + 容差比较
  • 极致性能:float + 较大容差

最后的忠告:
浮点数的问题不是Java独有的,而是计算机科学的基本问题。理解了IEEE 754,你就理解了为什么0.1 + 0.2 ≠ 0.3。

那个深夜的线上Bug,虽然让我加班到凌晨3点,但也让我彻底理解了浮点数的本质。现在每当看到有同事写if (price == expectedPrice)这样的代码,我都会毫不犹豫地拍他肩膀:

"兄弟,永远不要相信浮点数相等!"

这句话,希望你们也能记住。
本文转自渣哥zha-ge.cn

點擊查看更多內(nèi)容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優(yōu)質(zhì)文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續(xù)努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優(yōu)惠券免費領(lǐng)

立即參與 放棄機會
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學習伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號

舉報

0/150
提交
取消