LUCENE-7185: fix tie-breaker sort bug

This commit is contained in:
Robert Muir 2016-04-07 11:26:46 -04:00
parent df07e0c30f
commit 05d62a3577
3 changed files with 38 additions and 15 deletions

View File

@ -37,7 +37,9 @@ public class SloppyMath {
* specified in decimal degrees (latitude/longitude). This works correctly
* even if the dateline is between the two points.
* <p>
* Error is around 1E-5 (0.01mm) from the actual haversine distance.
* Error is at most 2E-1 (20cm) from the actual haversine distance, but is typically
* much smaller for reasonable distances: around 1E-5 (0.01mm) for distances less than
* 1000km.
*
* @param lat1 Latitude of the first point.
* @param lon1 Longitude of the first point.
@ -87,7 +89,9 @@ public class SloppyMath {
double x2 = lat2 * TO_RADIANS;
double h1 = 1 - cos(x1 - x2);
double h2 = 1 - cos((lon1 - lon2) * TO_RADIANS);
return h1 + cos(x1) * cos(x2) * h2;
double h = h1 + cos(x1) * cos(x2) * h2;
// clobber crazy precision so subsequent rounding does not create ties.
return Double.longBitsToDouble(Double.doubleToRawLongBits(h) & 0xFFFFFFFFFFFFFFF8L);
}
/**

View File

@ -24,6 +24,8 @@ import static org.apache.lucene.util.SloppyMath.haversinSortKey;
import java.util.Random;
import org.apache.lucene.geo.GeoTestUtil;
public class TestSloppyMath extends LuceneTestCase {
// accuracy for cos()
@ -31,7 +33,9 @@ public class TestSloppyMath extends LuceneTestCase {
// accuracy for asin()
static double ASIN_DELTA = 1E-7;
// accuracy for haversinMeters()
static double HAVERSIN_DELTA = 1E-5;
static double HAVERSIN_DELTA = 2E-1;
// accuracy for haversinMeters() for "reasonable" distances (< 1000km)
static double REASONABLE_HAVERSIN_DELTA = 1E-5;
public void testCos() {
assertTrue(Double.isNaN(cos(Double.NaN)));
@ -127,14 +131,14 @@ public class TestSloppyMath extends LuceneTestCase {
/** Test this method sorts the same way as real haversin */
public void testHaversinSortKey() {
for (int i = 0; i < 100000; i++) {
double centerLat = -90 + 180.0 * random().nextDouble();
double centerLon = -180 + 360.0 * random().nextDouble();
double centerLat = GeoTestUtil.nextLatitude();
double centerLon = GeoTestUtil.nextLongitude();
double lat1 = -90 + 180.0 * random().nextDouble();
double lon1 = -180 + 360.0 * random().nextDouble();
double lat1 = GeoTestUtil.nextLatitude();
double lon1 = GeoTestUtil.nextLongitude();
double lat2 = -90 + 180.0 * random().nextDouble();
double lon2 = -180 + 360.0 * random().nextDouble();
double lat2 = GeoTestUtil.nextLatitude();
double lon2 = GeoTestUtil.nextLongitude();
int expected = Integer.signum(Double.compare(haversinMeters(centerLat, centerLon, lat1, lon1),
haversinMeters(centerLat, centerLon, lat2, lon2)));
@ -148,10 +152,10 @@ public class TestSloppyMath extends LuceneTestCase {
public void testAgainstSlowVersion() {
for (int i = 0; i < 100_000; i++) {
double lat1 = -90 + 180.0 * random().nextDouble();
double lon1 = -180 + 360.0 * random().nextDouble();
double lat2 = -90 + 180.0 * random().nextDouble();
double lon2 = -180 + 360.0 * random().nextDouble();
double lat1 = GeoTestUtil.nextLatitude();
double lon1 = GeoTestUtil.nextLongitude();
double lat2 = GeoTestUtil.nextLatitude();
double lon2 = GeoTestUtil.nextLongitude();
double expected = haversinMeters(lat1, lon1, lat2, lon2);
double actual = slowHaversin(lat1, lon1, lat2, lon2);
@ -159,6 +163,21 @@ public class TestSloppyMath extends LuceneTestCase {
}
}
public void testAgainstSlowVersionReasonable() {
for (int i = 0; i < 100_000; i++) {
double lat1 = GeoTestUtil.nextLatitude();
double lon1 = GeoTestUtil.nextLongitude();
double lat2 = GeoTestUtil.nextLatitude();
double lon2 = GeoTestUtil.nextLongitude();
double expected = haversinMeters(lat1, lon1, lat2, lon2);
if (expected < 1_000_000) {
double actual = slowHaversin(lat1, lon1, lat2, lon2);
assertEquals(expected, actual, REASONABLE_HAVERSIN_DELTA);
}
}
}
// simple incorporation of the wikipedia formula
private static double slowHaversin(double lat1, double lon1, double lat2, double lon2) {
double h1 = (1 - StrictMath.cos(StrictMath.toRadians(lat2) - StrictMath.toRadians(lat1))) / 2;

View File

@ -64,7 +64,7 @@ public class TestLatLonPointDistanceSort extends LuceneTestCase {
TopDocs td = searcher.search(new MatchAllDocsQuery(), 3, sort);
FieldDoc d = (FieldDoc) td.scoreDocs[0];
assertEquals(462.1028401330432, (Double)d.fields[0], 0.0D);
assertEquals(462.1028401330431, (Double)d.fields[0], 0.0D);
d = (FieldDoc) td.scoreDocs[1];
assertEquals(1054.9842850974826, (Double)d.fields[0], 0.0D);
@ -101,7 +101,7 @@ public class TestLatLonPointDistanceSort extends LuceneTestCase {
TopDocs td = searcher.search(new MatchAllDocsQuery(), 3, sort);
FieldDoc d = (FieldDoc) td.scoreDocs[0];
assertEquals(462.1028401330432D, (Double)d.fields[0], 0.0D);
assertEquals(462.1028401330431D, (Double)d.fields[0], 0.0D);
d = (FieldDoc) td.scoreDocs[1];
assertEquals(1054.9842850974826, (Double)d.fields[0], 0.0D);