为什么不应使用 double 表示金额
浮点数精度问题
为什么不应使用 double 表示金额 是 CoddyKit 上的免费 Java Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
危险的默认选择
将价格存储在 double 中似乎很自然。遗憾的是,double 无法精确表示大多数十进制小数,这会产生细微误差,破坏财务计算。
经典意外
使用 double 将 0.1 和 0.2 相加,得到的并不是 0.3,而是一个带有微小舍入误差的值。
public class Main {
public static void main(String[] args) {
double result = 0.1 + 0.2;
System.out.println(result);
}
}原因
计算机以二进制存储 double。正如三分之一无法用十进制精确表示一样,许多小数(例如 0.1)也无法用二进制精确表示。
计算机会存储最接近的二进制近似值,而误差会不断累积。
误差不断累积
单个微小误差可能看起来无伤大雅,但在遍历数千笔交易的循环中,这些差异会累积成明显的错误。
public class Main {
public static void main(String[] args) {
double total = 0.0;
for (int i = 0; i < 10; i++) {
total += 0.1;
}
System.out.println(total);
}
}相等比较失败
由于舍入误差,使用 == 比较计算得到的双精度浮点数时,如果您预期它们相等,比较结果往往会失败。
public class Main {
public static void main(String[] args) {
double a = 0.1 + 0.2;
double b = 0.3;
System.out.println(a == b);
}
}金额必须精确
在金融领域,哪怕只有几分之一美分的误差也是不可接受的。审计人员希望总额精确到每一分钱。
因此,绝不能使用浮点类型存储金额。
解决方案:BigDecimal
Java 提供了 BigDecimal,这是一个能够精确存储十进制数的类。它分别记录未缩放值和 scale,因此 0.1 真正表示的就是 0.1。
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b));
}
}始终使用字符串构造方式
请从字符串而不是 double 创建 BigDecimal。传入 double 会将二进制误差带入 BigDecimal。
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal fromDouble = new BigDecimal(0.1);
BigDecimal fromString = new BigDecimal("0.1");
System.out.println(fromDouble);
System.out.println(fromString);
}
}或者使用 valueOf
BigDecimal.valueOf 是一种安全的快捷方式。它使用 double 的规范字符串形式进行转换,从而避免原始二进制误差。
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal price = BigDecimal.valueOf(19.99);
System.out.println(price);
}
}将金额按分存储
对于简单情况,另一种选择是使用 long 将金额存储为整数分数。这样,19.99 美元就变成 1999 分,所有运算都是精确的整数运算。
public class Main {
public static void main(String[] args) {
long cents = 1999;
long total = cents * 3;
System.out.println(total + " cents");
}
}要点
在科学和图形处理等允许小误差的场景中,请使用 float 和 double。对于金额以及任何必须精确的值,请使用 BigDecimal 或整数分。
快速检查
测试您对浮点数 precision 的理解。
回顾
您已经了解 double 为什么不适合金额:
- 二进制浮点数无法精确存储许多十进制小数
- 像
0.1 + 0.2不等于 0.3 这样的误差会不断累积 - 使用
BigDecimal进行精确的十进制运算 - 从字符串或使用
valueOf创建它,绝不要直接从原始 double 创建 - 存储整数分是另一种精确的选择
常见问题解答
「为什么不应使用 double 表示金额」课时是免费的吗?
是的 — 「为什么不应使用 double 表示金额」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「为什么不应使用 double 表示金额」这节课中我会学到什么?
浮点数精度问题 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「为什么不应使用 double 表示金额」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 为什么不应使用 double 表示金额
- 使用 BigDecimal
- 舍入与刻度
- 使用 BigInteger 处理超大数字