Skip to content

Commit

Permalink
Fix root-node precision problems
Browse files Browse the repository at this point in the history
  • Loading branch information
tzaeschke committed Jun 30, 2024
1 parent f55e4b8 commit 97d1f45
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private void adjustRootSize(double[] key) {
for (int i = 0; i < root.getValueCount(); i++) {
dMax = Math.max(dMax, MathTools.maxDelta(root.getValues()[i].point(), root.getCenter()));
}
double radius = MathTools.floorPowerOfTwo(dMax) * 2;
double radius = MathTools.ceilPowerOfTwo(dMax + QUtil.EPS_MUL);
if (radius > 0) {
root.adjustRadius(radius);
} else if (root.getValueCount() >= maxNodeSize - 1) {
Expand Down
16 changes: 14 additions & 2 deletions src/main/java/org/tinspin/index/util/MathTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,23 @@ public class MathTools {

private MathTools() {}

/**
* Similar to Math.ceil() with the ceiling being the next higher power of 2.
* The resulting number can repeatedly and (almost) always be divided by two without loss of precision.
* @param d input
* @return next power of two above or equal to 'input'
*/
public static double ceilPowerOfTwo(double d) {
double ceil = floorPowerOfTwo(d);
return ceil == d ? ceil : ceil * 2;
}

/**
* Similar to Math.floor() with the floor being the next lower power of 2.
* The resulting number can repeatedly (almost) always be divided by two without loss of precision.
* The resulting number can repeatedly and (almost) always be divided by two without loss of precision.
* We calculate the "floor" by setting the "fraction" of the bit representation to 0.
* @param d input
* @return next lower power of two below 'input'
* @return next power of two below or equal to 'input'
*/
public static double floorPowerOfTwo(double d) {
// Set fraction to "0".
Expand Down
35 changes: 35 additions & 0 deletions src/test/java/org/tinspin/util/MathToolsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,48 @@

public class MathToolsTest {

@Test
public void powerOfTwoCeil() {
assertEquals(1./32., MathTools.ceilPowerOfTwo(0.03), 0.0);
assertEquals(0.5, MathTools.ceilPowerOfTwo(0.3), 0.0);
assertEquals(4, MathTools.ceilPowerOfTwo(3), 0.0);
assertEquals(32, MathTools.ceilPowerOfTwo(30), 0.0);
assertEquals(512, MathTools.ceilPowerOfTwo(300), 0.0);

assertEquals(-0.5, MathTools.ceilPowerOfTwo(-0.3), 0.0);
assertEquals(-4, MathTools.ceilPowerOfTwo(-3), 0.0);
assertEquals(-32, MathTools.ceilPowerOfTwo(-30), 0.0);

// identity
assertEquals(0, MathTools.ceilPowerOfTwo(0), 0.0);
assertEquals(-0.5, MathTools.ceilPowerOfTwo(-0.5), 0.0);
assertEquals(0.5, MathTools.ceilPowerOfTwo(0.5), 0.0);
assertEquals(-1, MathTools.ceilPowerOfTwo(-1), 0.0);
assertEquals(1, MathTools.ceilPowerOfTwo(1), 0.0);
assertEquals(-2, MathTools.ceilPowerOfTwo(-2), 0.0);
assertEquals(2, MathTools.ceilPowerOfTwo(2), 0.0);
}

@Test
public void powerOfTwoFloor() {
assertEquals(1./64., MathTools.floorPowerOfTwo(0.03), 0.0);
assertEquals(0.25, MathTools.floorPowerOfTwo(0.3), 0.0);
assertEquals(2, MathTools.floorPowerOfTwo(3), 0.0);
assertEquals(16, MathTools.floorPowerOfTwo(30), 0.0);
assertEquals(256, MathTools.floorPowerOfTwo(300), 0.0);

assertEquals(-0.25, MathTools.floorPowerOfTwo(-0.3), 0.0);
assertEquals(-2, MathTools.floorPowerOfTwo(-3), 0.0);
assertEquals(-16, MathTools.floorPowerOfTwo(-30), 0.0);

// identity
assertEquals(0, MathTools.ceilPowerOfTwo(0), 0.0);
assertEquals(-0.5, MathTools.ceilPowerOfTwo(-0.5), 0.0);
assertEquals(0.5, MathTools.ceilPowerOfTwo(0.5), 0.0);
assertEquals(-1, MathTools.ceilPowerOfTwo(-1), 0.0);
assertEquals(1, MathTools.ceilPowerOfTwo(1), 0.0);
assertEquals(-2, MathTools.ceilPowerOfTwo(-2), 0.0);
assertEquals(2, MathTools.ceilPowerOfTwo(2), 0.0);
}

@Test
Expand Down

0 comments on commit 97d1f45

Please sign in to comment.