引子
起因是有群友在尝试解决 double 不够存数据的问题,看到了 decimal,但对 decimal 的理解还是不对。
这段话说的很含糊,可能是从 ai 的文本中截取的一段。而问为什么要用 decimal 时,这也是一般人的回答。
清晰的解释
浮点数在计算机中以二进制形式存储,而很多十进制小数(例如 0.1
)在二进制下表示为无限循环小数。由于浮点数的存储空间有限,这些无限二进制小数必须被截断,从而引入精度误差。相比之下,decimal
类型使用十进制存储,可以精确表示十进制小数,因此不会产生类似的精度问题,适合对精度要求高的场景(如财务计算)。
进制与截断
decimal 如其名一样是十进制的,而一般的浮点(float/double)是基于二进制(binary floating-point)遵循 IEEE 754 标准的。
最常见的情况是 0.1 + 0.2 = 0.3 的例子,请参考以下代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void main(String[] args) { checkFiniteInBinary("0.1"); checkFiniteInBinary("0.2"); checkFiniteInBinary("0.3"); checkFiniteInBinary("0.5");
float floatResult = .1f + .2f; System.out.printf("floatResult = %.64f\n", floatResult);
double doubleResult = .1 + .2; System.out.printf("doubleResult = %.64f\n", doubleResult);
BigDecimal decimalResult = new BigDecimal("0.1").add(new BigDecimal("0.2")); System.out.printf("decimalResult = %.64f\n", decimalResult);
doubleResult = .2 + .3; System.out.printf("doubleResult = %.64f\n", doubleResult); }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 19:53:38: Executing ':org.ryuu.Main.main()'…
Starting Gradle Daemon... Gradle Daemon started in 1 s 634 ms > Task :compileJava UP-TO-DATE > Task :processResources NO-SOURCE > Task :classes UP-TO-DATE
> Task :org.ryuu.Main.main() 十进制数0.1在二进制数下不能有限表示。 0.0001100110011001100110011001100110011001100110011001100110011001... (仅显示64位)
十进制数0.2在二进制数下不能有限表示。 0.0011001100110011001100110011001100110011001100110011001100110011... (仅显示64位)
十进制数0.3在二进制数下不能有限表示。 0.0100110011001100110011001100110011001100110011001100110011001100... (仅显示64位)
十进制数0.5在二进制数下能有限表示。 0.1
floatResult = 0.3000000119209289600000000000000000000000000000000000000000000000 doubleResult = 0.3000000000000000400000000000000000000000000000000000000000000000 decimalResult = 0.3000000000000000000000000000000000000000000000000000000000000000 doubleResult = 0.5000000000000000000000000000000000000000000000000000000000000000
BUILD SUCCESSFUL in 6s 2 actionable tasks: 1 executed, 1 up-to-date 19:53:46: Execution finished ':org.ryuu.Main.main()'.
|
0.1,0.2这样的数在二进制里类似十进制的1/3(0.3333333…),是无限循环的。浮点数会将数据截断,因此会丢失精度。
1 2
| 0.1(decimal) = 0.0001100110011001100110011001100110011001100110011001100110011001... (binary) 0.2(decimal) = 0.0011001100110011001100110011001100110011001100110011001100110011... (binary)
|
另请参阅
github 有完整的示例工程