java 为什么指定 BigDecimal.equals 来分别比较值和比例?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14102083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is BigDecimal.equals specified to compare both value and scale individually?
提问by bacar
This is not a question about how to compare two BigDecimal
objects - I know that you can use compareTo
instead of equals
to do that, since equals
is documented as:
这不是关于如何比较两个BigDecimal
对象的问题 - 我知道您可以使用compareTo
而不是equals
这样做,因为equals
记录为:
Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).
与 compareTo 不同,此方法仅在两个 BigDecimal 对象的值和比例相等时才认为它们相等(因此,通过此方法进行比较时,2.0 不等于 2.00)。
The question is: why has the equals
been specified in this seemingly counter-intuitive manner? That is, why is it importantto be able to distinguish between 2.0 and 2.00?
问题是:为什么equals
以这种看似违反直觉的方式指定?也就是说,为什么能够区分 2.0 和 2.00很重要?
It seems likely that there must be a reason for this, since the Comparable
documentation, which specifies the compareTo
method, states:
这似乎一定是有原因的,因为Comparable
指定该compareTo
方法的文档指出:
It is strongly recommended (though not required) that natural orderings be consistent with equals
强烈建议(虽然不是必需的)自然顺序与 equals 一致
I imagine there must be a good reason for ignoring this recommendation.
我想忽略这个建议一定有很好的理由。
采纳答案by Oliver Charlesworth
Because in some situations, an indication of precision (i.e. the margin of error) may be important.
因为在某些情况下,精度指示(即误差幅度)可能很重要。
For example, if you're storing measurements made by two physical sensors, perhaps one is 10x more precise than the other. It may be important to represent this fact.
例如,如果您要存储由两个物理传感器进行的测量,则其中一个可能比另一个精确 10 倍。代表这一事实可能很重要。
回答by supercat
A point which has not yet been considered in any of the other answers is that equals
is required to be consistent with hashCode
, and the cost of a hashCode
implementation which was required to yield the same value for 123.0 as for 123.00 (but still do a reasonable job of distinguishing different values) would be much greater than that of a hashCode implementation which was not required to do so. Under the present semantics, hashCode
requires a multiply-by-31 and add for each 32 bits of stored value. If hashCode
were required to be consistent among values with different precision, it would either have to compute the normalized form of any value (expensive) or else, at minimum, do something like compute the base-999999999 digital root of the value and multiply that, mod 999999999, based upon the precision. The inner loop of such a method would be:
这还没有在任何其他的答案中考虑到的一点是,equals
需要与其保持一致hashCode
,并且成本hashCode
也是一个需要,从而得到该值123.0为123.00(但仍实现做一个合理的工作区分不同的值)将比不需要这样做的 hashCode 实现大得多。在当前语义下,hashCode
需要对每 32 位存储值进行乘以 31 和相加。如果hashCode
需要在具有不同精度的值之间保持一致,它要么必须计算任何值的归一化形式(昂贵),要么至少做一些事情,例如计算该值的 base-999999999 数字根并将其相乘,mod 999999999,基于精度。这种方法的内部循环是:
temp = (temp + (mag[i] & LONG_MASK) * scale_factor[i]) % 999999999;
replacing a multiply-by-31 with a 64-bit modulus operation--much more expensive. If one wants a hash table which regards numerically-equivalent BigDecimal
values as equivalent, and most keys which are sought in the table will be found, the efficient way to achieve the desired result would be to use a hash table which stores value wrappers, rather than storing values directly. To find a value in the table, start by looking for the value itself. If none is found, normalize the value and look for that. If nothing is found, create an empty wrapper and store an entry under the original and normalized forms of the number.
用 64 位模数运算替换乘以 31——要贵得多。如果想要一个将数字等价的BigDecimal
值视为等价的哈希表,并且可以找到在表中查找的大多数键,则实现所需结果的有效方法是使用存储值包装器的哈希表,而不是直接存储值。要在表中查找值,请先查找值本身。如果未找到,则对值进行标准化并查找。如果未找到任何内容,请创建一个空包装器并在数字的原始和规范化形式下存储一个条目。
Looking for something which isn't in the table and hasn't been searched for previously would require an expensive normalization step, but looking for something that has been searched for would be much faster. By contrast, if HashCode needed to return equivalent values for numbers which, because of differing precision, were stored totally differently, that would make all hash table operations much slower.
查找不在表中且以前未搜索过的内容将需要昂贵的标准化步骤,但查找已搜索过的内容会快得多。相比之下,如果 HashCode 需要为由于精度不同而存储完全不同的数字返回等效值,那么所有哈希表操作都会变得更慢。
回答by Aleksander Blomsk?ld
In math, 10.0 equals 10.00. In physics 10.0m and 10.00m are arguably different (different precision), when talking about objects in an OOP, I would definitely say that they are not equal.
在数学上,10.0 等于 10.00。在物理学中,10.0m 和 10.00m 可以说是不同的(不同的精度),当谈到 OOP 中的对象时,我肯定会说它们不相等。
It's also easy to think of unexpected functionality if equals ignored the scale (For instance: if a.equals(b), wouldn't you expect a.add(0.1).equals(b.add(0.1)?).
如果 equals 忽略了比例,也很容易想到意外的功能(例如:如果 a.equals(b),你会不会期望 a.add(0.1).equals(b.add(0.1)?)。
回答by assylias
If numbers get rounded, it shows the precision of the calculation - in other words:
如果数字四舍五入,它会显示计算的精度 - 换句话说:
- 10.0 could mean that the exact number was between 9.95 and 10.05
- 10.00 could mean that the exact number was between 9.995 and 10.005
- 10.0 可能意味着确切的数字在 9.95 和 10.05 之间
- 10.00 可能意味着确切的数字在 9.995 和 10.005 之间
In other words, it is linked to arithmetic precision.
换句话说,它与算术精度有关。
回答by Matt R
I imagine there must be a good reason for ignoring this recommendation.
我想忽略这个建议一定有很好的理由。
Maybe not. I propose the simple explanation that the designers of BigDecimal
just made a bad design choice.
也许不会。我提出了一个简单的解释,即设计师BigDecimal
刚刚做出了一个糟糕的设计选择。
- A good design optimises for the common use case. The majority of the time (>95%), people want to compare two quantities based on mathematical equality. For the minority of the time where you really do care about the two numbers being equal in both scale and value, there could have been an additional method for that purpose.
- It goes against people's expectations, and creates a trap that's very easy to fall into. A good API obeys the "principle of least surprise".
- It breaks the usual Java convention that
Comparable
is consistent with equality.
- 一个好的设计会针对常见用例进行优化。大多数情况下 (>95%),人们希望根据数学相等来比较两个数量。在少数情况下,您确实关心两个数字在规模和价值上是否相等,为此可能还有其他方法。
- 它违背了人们的期望,并且制造了一个非常容易掉入的陷阱。一个好的 API 遵循“最小惊喜原则”。
- 它打破了
Comparable
与平等一致的通常的 Java 约定。
Interestingly, Scala's BigDecimal
class (which is implemented using Java's BigDecimal
under the hood) has made the opposite choice:
有趣的是,Scala 的BigDecimal
类(在底层使用 Java 实现BigDecimal
)做出了相反的选择:
BigDecimal("2.0") == BigDecimal("2.00") // true
回答by supercat
The compareTo
method knows that trailing zeros do not affect the numeric value represented by a BigDecimal
, which is the only aspect compareTo
cares about. By contrast, the equals
method generally has no way of knowing what aspects of an object someone cares about, and should thus only return true
if two objects are equivalent in everyway that a programmer might be interested in. If x.equals(y)
is true, it would be rather surprising for x.toString().equals(y.toString())
to yield false.
该compareTo
方法知道尾随零不会影响 a 表示的数值BigDecimal
,这是唯一compareTo
关心的方面。相比之下,该equals
方法通常无法知道某人关心对象的哪些方面,因此只有true
当两个对象在程序员可能感兴趣的各个方面都等效时才应该返回。如果x.equals(y)
为真,那将是相当令人惊讶的为x.toString().equals(y.toString())
产生假。
Another issue which is perhaps even more significant is that BigDecimal
essentially combines a BigInteger
and a scaling factor, such that if two numbers represent the same value but have different numbers of trailing zeroes, one will hold a bigInteger
whose value is some power of ten times the other. If equality requires that the mantissa and scale both match, then the hashCode()
for BigDecimal
can use the hash code of BigInteger
. If it's possible for two values to be considered "equal" even though they contain different BigInteger
values, however, that will complicate things significantly. A BigDecimal
type which used its own backing storage, rather than a BigInteger
, could be implemented in a variety of ways to allow numbers to be quickly hashed in such a way that values representing the same number would compare equal (as a simple example, a version which packed nine decimal digits in each long
value and always required that the decimal point sit between groups of nine, could compute the hash code in a way that would ignore trailing groups whose value was zero) but a BigDecimal
that encapsulates a BigInteger
can't do that.
另一个可能更重要的问题是BigDecimal
本质上结合了 aBigInteger
和缩放因子,这样如果两个数字表示相同的值但具有不同数量的尾随零,则一个将持有bigInteger
其值是另一个的十次幂的a 。如果相等要求尾数和小数位数都匹配,则hashCode()
forBigDecimal
可以使用 的哈希码BigInteger
。但是,如果两个值即使包含不同的BigInteger
值也可能被视为“相等” ,那将使事情变得非常复杂。甲BigDecimal
其中使用其自己的后备存储器,而不是一种类型BigInteger
, 可以通过多种方式实现,以允许数字以这样一种方式快速散列,即表示相同数字的值比较相等(作为一个简单的例子,一个版本在每个long
值中包含九个十进制数字并且总是要求小数点位于九组之间,可以以忽略值为零的尾随组的方式计算哈希码,但是BigDecimal
封装了 a 的 aBigInteger
不能这样做。