Improve Precision for scaled_float (#37169)

* Use `toString` and `Bigdecimal` parsing to get intuitive behaviour for `scaled_float` as discussed in #32570
* Closes #32570
This commit is contained in:
Armin Braun 2019-01-11 08:07:55 +01:00 committed by GitHub
parent 822626dadf
commit 860a8a7b23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 6 deletions

View File

@ -62,6 +62,7 @@ import org.elasticsearch.search.MultiValueMode;
import org.joda.time.DateTimeZone;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@ -227,8 +228,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
@Override
public Query termQuery(Object value, QueryShardContext context) {
failIfNotIndexed();
double queryValue = parse(value);
long scaledValue = Math.round(queryValue * scalingFactor);
long scaledValue = Math.round(scale(value));
Query query = NumberFieldMapper.NumberType.LONG.termQuery(name(), scaledValue);
if (boost() != 1f) {
query = new BoostQuery(query, boost());
@ -241,8 +241,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
failIfNotIndexed();
List<Long> scaledValues = new ArrayList<>(values.size());
for (Object value : values) {
double queryValue = parse(value);
long scaledValue = Math.round(queryValue * scalingFactor);
long scaledValue = Math.round(scale(value));
scaledValues.add(scaledValue);
}
Query query = NumberFieldMapper.NumberType.LONG.termsQuery(name(), Collections.unmodifiableList(scaledValues));
@ -257,7 +256,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
failIfNotIndexed();
Long lo = null;
if (lowerTerm != null) {
double dValue = parse(lowerTerm) * scalingFactor;
double dValue = scale(lowerTerm);
if (includeLower == false) {
dValue = Math.nextUp(dValue);
}
@ -265,7 +264,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
}
Long hi = null;
if (upperTerm != null) {
double dValue = parse(upperTerm) * scalingFactor;
double dValue = scale(upperTerm);
if (includeUpper == false) {
dValue = Math.nextDown(dValue);
}
@ -326,6 +325,19 @@ public class ScaledFloatFieldMapper extends FieldMapper {
public int hashCode() {
return 31 * super.hashCode() + Double.hashCode(scalingFactor);
}
/**
* Parses input value and multiplies it with the scaling factor.
* Uses the round-trip of creating a {@link BigDecimal} from the stringified {@code double}
* input to ensure intuitively exact floating point operations.
* (e.g. for a scaling factor of 100, JVM behaviour results in {@code 79.99D * 100 ==> 7998.99..} compared to
* {@code scale(79.99) ==> 7999})
* @param input Input value to parse floating point num from
* @return Scaled value
*/
private double scale(Object input) {
return new BigDecimal(Double.toString(parse(input))).multiply(BigDecimal.valueOf(scalingFactor)).doubleValue();
}
}
private Explicit<Boolean> ignoreMalformed;

View File

@ -140,6 +140,8 @@ public class ScaledFloatFieldTypeTests extends FieldTypeTestCase {
assertEquals("scaled_float:[-9223372036854775808 TO 10]", scaledFloatQ.toString());
scaledFloatQ = ft.rangeQuery(null, 0.105, true, true, null);
assertEquals("scaled_float:[-9223372036854775808 TO 10]", scaledFloatQ.toString());
scaledFloatQ = ft.rangeQuery(null, 79.99, true, true, null);
assertEquals("scaled_float:[-9223372036854775808 TO 7999]", scaledFloatQ.toString());
}
public void testRoundsLowerBoundCorrectly() {