java - 双包装类的 .equals() 方法是否可用于查找浮点数的相等性?
问题描述
我知道对于原始浮点类型(浮点数和双精度数),您不应该直接通过==
. 但是如果你使用双倍的包装类呢?会像
Double a = 5.05;
Double b = 5.05;
boolean test = a.equals(b);
正确比较这两个值?
解决方案
您需要完全理解为什么==
比较是一个坏主意的原因。没有理解,你只是在黑暗中摸索。
让我们谈谈计算机(和替身)是如何工作的。
想象你进入一个房间;它有3个灯开关,否则它是光秃秃的。你将进入房间,你可以摆弄开关,但你必须离开。我稍后进入房间,可以查看开关。
你能传达多少信息?
答案是:您可以传达 8 种不同的状态:DDD、DDU、DUD、DUU、UDD、UDU、UUD 和 UUU。就是这样。
当计算机存储双精度时,计算机的工作方式与此完全相同。除了 3 个开关,你得到 64 个开关。这意味着2^64
您可以使用单个 来传达不同的状态double
,这是大量的状态:这是一个 19 位数字。
但它仍然是有限数量的状态,这是有问题的:0到1之间的数字是无限的。更不用说-infinity和+infinity之间了,它double
敢于覆盖。当你只代表状态时,你如何存储无限的选择之一2^64
?当这个 19 位数字被要求从无限的可能性中区分出来时,它开始看起来非常小,不是吗?
答案当然是这完全不可能。
所以双打实际上并不是这样工作的。取而代之的是,有人费了些力气,在一个大房间里挂了一条巨大的数字线,从负无穷到正无穷,向2^64
这条线投掷飞镖。他们登陆的数字是“有福的数字”——这些数字可以用一个double
值来表示。这确实意味着任何两个飞镖之间都有无限数量的数字,因此无法表示。飞镖不是很随机:越接近 0,飞镖越密集。一旦超过大约2^52
2 支飞镖之间的距离甚至超过 1.0。
这是一个不可表示的数字的简单示例:0.3。很神奇,不是吗?这么简单的事情。这意味着计算机实际上无法使用double
. 那么当你尝试时会发生什么?规则规定,任何计算的结果总是默默地四舍五入到最接近的祝福数字。
这就是问题所在:您可以运行数学:
double x = 0.1 + 0.2;
然后做:
double y = 0.9 - 0.8 + 0.15 + 0.05;
我们人类会立即注意到这一点x
,并且y
自然是相同的。但对于计算机而言并非如此——因为无声四舍五入到最接近的祝福数字,它可能x
是0.29999999999999999785
,并且y
是0.300000000000000000012
。
因此,我们在使用时会遇到四个关键方面double
(或者float
在每种方式中都更糟,永远不要使用这些方面):
- 如果您需要绝对精度,请不要使用它们。
- 打印它们时,请始终将它们四舍五入。System.out.println 开箱即用,但您应该真正使用
.printf("%.5f")
或类似:选择您需要的数字#。 - 请注意,错误会更加复杂,并且随着您离 1.0 越来越远,它会变得更糟。
- 永远不要与 进行比较
==
,而是始终使用 delta-compare:“如果两个数字在彼此的 0.0000000001 范围内,则让我们认为它们相等”的概念。
没有通用的魔法增量值;这取决于您的精度需求,您距离 1.0 的距离等。因此,只需询问计算机:嘿,弄清楚这些东西我只想知道这两个双打是否相等是不可能的。唯一不需要您输入它们“有多接近”的可用定义是绝对完美的概念:只有当它们完全相同时它们才相等。在上面那个简单的例子中,这个定义会让你失望。如果您使用Double.equals
instead ofdouble == double
或任何其他实用程序类,它不会有任何不同。
所以,不,Double.equals
不适合。您将不得不比较Math.abs(d1 - d2) < epsilon
,其中 epsilon 是您的选择。大多数情况下,如果平等很重要,那么您已经做错了,并且不应该首先使用double
。
NB:当代表金钱时,你不想要不可预测的四舍五入,所以永远不要使用双精度数。相反,找出原子银行单位是什么(美元、欧分、日元、比特币的聪等),并将其存储为 long。您存储 4.52 美元long x = 452;
,而不是double x = 4.52;
.
推荐阅读
- wordpress - WordPress 多站点子站点 WP 管理员不工作
- c# - 使用python将音频文件发送到api
- jquery - JQuery Uncaught TypeError: $(...).resizable is not a function
- python - 索引大型json时出现python elasticsearch超时错误
- php - 删除链接文本mysql的最后一部分
- c# - 在 .NET Standard 上实现异步返回类型
- sql - 我需要一个包含前几行总和的新行
- r - mclust 参数的方差/sigma 的无限值
- php - 使用 SQLite3 数据库进行 PHP 搜索查询
- xslt - XSLT - 如果语句没有在 for-each 中触发