为何Java话费计算程序返回6.999999999999993而非预期值7?
问题原因:浮点数的二进制精度缺陷
这是因为double(以及所有二进制浮点数)无法精确表示0.1这个十进制小数——就像十进制里没法精确写出1/3(0.333...)一样,0.1转成二进制是无限循环的小数,double只能存储它的近似值。你每次循环累加这个近似值,误差会不断累积,20次累加后就出现了明显的偏差,导致结果变成6.999999999999993而不是预期的7。
而0.5是2的负一次方(1/2),能被二进制精确表示,所以累加的时候不会有误差,结果自然正确。
解决方案
推荐下面几种彻底解决的办法:
1. 用整数处理金额(最稳妥)
把所有金额单位换成美分,用int或long计算,最后再转成美元。完全规避浮点数误差:
import java.util.*; public class MobilePhoneCharges { public static void main(String[] args) { int cents = 0; // 用美分做单位 String userInput; Scanner sc = new Scanner(System.in); System.out.print("Hello! Welcome to MOBILEPHONECOMPANY! \nPlease enter the type of customer you are! "); userInput = sc.nextLine(); if(userInput.equals("r") || userInput.equals("c") || userInput.equals("p") ) { System.out.println("Please enter in how many minutes you have used! "); int minutes = sc.nextInt(); if (userInput.equals("r") && minutes>0) { cents += 500; // 5美元 = 500美分 if(minutes > 60) { int extraMinutes = minutes - 60; cents += extraMinutes * 10; // 每分钟10美分 } // 转成美元输出 System.out.printf("You owe %.2f!\n", cents / 100.0); } } sc.close(); } }
2. 使用BigDecimal精确计算
如果必须用小数,用BigDecimal代替double,指定精确的舍入规则:
import java.math.BigDecimal; import java.util.*; public class MobilePhoneCharges { public static void main(String[] args) { BigDecimal money = BigDecimal.ZERO; String userInput; Scanner sc = new Scanner(System.in); System.out.print("Hello! Welcome to MOBILEPHONECOMPANY! \nPlease enter the type of customer you are! "); userInput = sc.nextLine(); if(userInput.equals("r") || userInput.equals("c") || userInput.equals("p") ) { System.out.println("Please enter in how many minutes you have used! "); int minutes = sc.nextInt(); if (userInput.equals("r") && minutes>0) { money = money.add(new BigDecimal("5.00")); if(minutes > 60) { int extraMinutes = minutes - 60; // 用字符串构造BigDecimal,避免浮点数传入的误差 money = money.add(new BigDecimal("0.10").multiply(new BigDecimal(extraMinutes))); } System.out.println("You owe "+money+"!"); } } sc.close(); } }
3. 优化计算逻辑(临时缓解)
不要用循环累加0.1,直接计算超出分钟数乘以0.1,减少误差次数(虽然还是可能有微小误差,但大部分场景下显示时可以通过格式化消除):
import java.util.*; public class MobilePhoneCharges { public static void main(String[] args) { double money = 0; String userInput; Scanner sc = new Scanner(System.in); System.out.print("Hello! Welcome to MOBILEPHONECOMPANY! \nPlease enter the type of customer you are! "); userInput = sc.nextLine(); if(userInput.equals("r") || userInput.equals("c") || userInput.equals("p") ) { System.out.println("Please enter in how many minutes you have used! "); int minutes = sc.nextInt(); if (userInput.equals("r") && minutes>0) { money += 5; if(minutes > 60) { int extraMinutes = minutes - 60; money += extraMinutes * 0.10; // 直接计算,不用循环 } // 格式化输出保留两位小数 System.out.printf("You owe %.2f!\n", money); } } sc.close(); } }
内容的提问来源于stack exchange,提问作者Mont Blac
相关产品推荐
相关产品推荐

