nchammas commented on code in PR #45036:
URL: https://github.com/apache/spark/pull/45036#discussion_r1482237064
##########
core/src/test/scala/org/apache/spark/util/collection/OpenHashMapSuite.scala:
##########
@@ -249,4 +249,34 @@ class OpenHashMapSuite extends SparkFunSuite with Matchers
{
map(null) = null
assert(map.get(null) === Some(null))
}
+
+ test("SPARK-45599: 0.0 and -0.0 should count distinctly") {
+ // Exactly these elements provided in roughly this order trigger a
condition where lookups of
+ // 0.0 and -0.0 in the bitset happen to collide, causing their counts to
be merged incorrectly
+ // and inconsistently if `==` is used to check for key equality.
+ val spark45599Repro = Seq(
+ Double.NaN,
+ 2.0,
+ 168.0,
+ Double.NaN,
+ Double.NaN,
+ -0.0,
+ 153.0,
+ 0.0
+ )
+
+ val map1 = new OpenHashMap[Double, Int]()
+ spark45599Repro.foreach(map1.changeValue(_, 1, {_ + 1}))
+ map1.iterator.foreach(println)
+ assert(map1(0.0) == 1)
+ assert(map1(-0.0) == 1)
+
+ val map2 = new OpenHashMap[Double, Int]()
+ // Simply changing the order in which the elements are added to the map
should not change the
+ // counts for 0.0 and -0.0.
+ spark45599Repro.reverse.foreach(map2.changeValue(_, 1, {_ + 1}))
+ map2.iterator.foreach(println)
+ assert(map2(0.0) == 1)
+ assert(map2(-0.0) == 1)
Review Comment:
This is another expression of the same bug that this PR addresses. If you
run this test on `master`, you will see that the counts for 0.0 and -0.0 depend
on the order in which the elements from `spark45599Repro` are added to the map.
##########
core/src/test/scala/org/apache/spark/util/collection/OpenHashSetSuite.scala:
##########
@@ -269,4 +269,42 @@ class OpenHashSetSuite extends SparkFunSuite with Matchers
{
assert(pos1 == pos2)
}
}
+
+ test("SPARK-45599: 0.0 and -0.0 are equal but not the same") {
+ // Therefore, 0.0 and -0.0 should get separate entries in the hash set.
+ //
+ // Exactly these elements provided in roughly this order will trigger the
following scenario:
+ // When probing the bitset in `getPos(-0.0)`, the loop will happen upon
the entry for 0.0.
+ // In the old logic pre-SPARK-45599, the loop will find that the bit is
set and, because
+ // -0.0 == 0.0, it will think that's the position of -0.0. But in reality
this is the position
+ // of 0.0. So -0.0 and 0.0 will be stored at different positions, but
`getPos()` will return
+ // the same position for them. This can cause users of OpenHashSet, like
OpenHashMap, to
+ // return the wrong value for a key based on whether or not this bitset
lookup collision
+ // happens.
Review Comment:
It really is amazing that @revans2 found this bug, because it depends on the
set being a specific size and the 0.0 and -0.0 being inserted and then looked
up in just the right order so that they happen to collide in the bitset.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]