Skip to content

Commit e07d545

Browse files
Fix the precision loss
Signed-off-by: Prudhvi Godithi <[email protected]>
1 parent c632a23 commit e07d545

File tree

1 file changed

+21
-24
lines changed

1 file changed

+21
-24
lines changed

modules/mapper-extras/src/main/java/org/opensearch/index/mapper/ScaledFloatFieldMapper.java

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
import org.opensearch.search.lookup.SearchLookup;
6666

6767
import java.io.IOException;
68-
import java.math.BigDecimal;
6968
import java.time.ZoneId;
7069
import java.util.ArrayList;
7170
import java.util.Arrays;
@@ -212,19 +211,23 @@ public byte[] encodePoint(Number value) {
212211
} else if (doubleValue == Double.NEGATIVE_INFINITY) {
213212
LongPoint.encodeDimension(Long.MIN_VALUE, point, 0);
214213
} else {
215-
LongPoint.encodeDimension(scaleToLong(doubleValue, scalingFactor), point, 0);
214+
LongPoint.encodeDimension(Math.round(scale(value)), point, 0);
216215
}
217216
return point;
218217
}
219218

219+
@Override
220220
public byte[] encodePoint(Object value, boolean roundUp) {
221-
double doubleValue = parse(value);
221+
long scaledValue = Math.round(scale(value));
222222
if (roundUp) {
223-
doubleValue = Math.nextUp(doubleValue);
223+
if (scaledValue < Long.MAX_VALUE) {
224+
scaledValue = scaledValue + 1;
225+
}
224226
} else {
225-
doubleValue = Math.nextDown(doubleValue);
227+
if (scaledValue > Long.MIN_VALUE) {
228+
scaledValue = scaledValue - 1;
229+
}
226230
}
227-
long scaledValue = scaleToLong(doubleValue, scalingFactor);
228231
byte[] point = new byte[Long.BYTES];
229232
LongPoint.encodeDimension(scaledValue, point, 0);
230233
return point;
@@ -242,7 +245,7 @@ public String typeName() {
242245
@Override
243246
public Query termQuery(Object value, QueryShardContext context) {
244247
failIfNotIndexedAndNoDocValues();
245-
long scaledValue = scaleToLong(parse(value), scalingFactor);
248+
long scaledValue = Math.round(scale(value));
246249
Query query = NumberFieldMapper.NumberType.LONG.termQuery(name(), scaledValue, hasDocValues(), isSearchable());
247250
if (boost() != 1f) {
248251
query = new BoostQuery(query, boost());
@@ -255,7 +258,7 @@ public Query termsQuery(List<?> values, QueryShardContext context) {
255258
failIfNotIndexedAndNoDocValues();
256259
List<Long> scaledValues = new ArrayList<>(values.size());
257260
for (Object value : values) {
258-
long scaledValue = scaleToLong(parse(value), scalingFactor);
261+
long scaledValue = Math.round(scale(value));
259262
scaledValues.add(scaledValue);
260263
}
261264
Query query = NumberFieldMapper.NumberType.LONG.termsQuery(
@@ -279,15 +282,15 @@ public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower
279282
if (includeLower == false) {
280283
dValue = Math.nextUp(dValue);
281284
}
282-
lo = scaleToLong(dValue, scalingFactor);
285+
lo = Math.round(Math.ceil(dValue));
283286
}
284287
Long hi = null;
285288
if (upperTerm != null) {
286289
double dValue = scale(upperTerm);
287290
if (includeUpper == false) {
288291
dValue = Math.nextDown(dValue);
289292
}
290-
hi = scaleToLong(dValue, scalingFactor);
293+
hi = Math.round(Math.floor(dValue));
291294
}
292295
Query query = NumberFieldMapper.NumberType.LONG.rangeQuery(name(), lo, hi, true, true, hasDocValues(), isSearchable(), context);
293296
if (boost() != 1f) {
@@ -327,8 +330,7 @@ protected Double parseSourceValue(Object value) {
327330
}
328331

329332
double scalingFactor = getScalingFactor();
330-
long scaledLong = scaleToLong(doubleValue, scalingFactor);
331-
return scaledLong / scalingFactor;
333+
return Math.round(doubleValue * scalingFactor) / scalingFactor;
332334
}
333335
};
334336
}
@@ -357,15 +359,16 @@ public DocValueFormat docValueFormat(String format, ZoneId timeZone) {
357359

358360
/**
359361
* Parses input value and multiplies it with the scaling factor.
360-
* Uses the round-trip of creating a {@link BigDecimal} from the stringified {@code double}
361-
* input to ensure intuitively exact floating point operations.
362-
* (e.g. for a scaling factor of 100, JVM behaviour results in {@code 79.99D * 100 ==> 7998.99..} compared to
363-
* {@code scale(79.99) ==> 7999})
362+
* Note: Uses direct floating-point multiplication for consistency
363+
* between indexing and querying. While this may result in
364+
* floating-point imprecision (e.g., 79.99 * 100 = 7998.999...),
365+
* the consistent behavior ensures search queries work correctly.
366+
*
364367
* @param input Input value to parse floating point num from
365368
* @return Scaled value
366369
*/
367370
private double scale(Object input) {
368-
return new BigDecimal(Double.toString(parse(input))).multiply(BigDecimal.valueOf(scalingFactor)).doubleValue();
371+
return parse(input) * scalingFactor;
369372
}
370373

371374
@Override
@@ -480,7 +483,7 @@ protected void parseCreateField(ParseContext context) throws IOException {
480483
throw new IllegalArgumentException("[scaled_float] only supports finite values, but got [" + doubleValue + "]");
481484
}
482485
}
483-
long scaledValue = scaleToLong(doubleValue, scalingFactor);
486+
long scaledValue = Math.round(doubleValue * scalingFactor);
484487

485488
List<Field> fields = NumberFieldMapper.NumberType.LONG.createFields(
486489
fieldType().name(),
@@ -501,12 +504,6 @@ static Double parse(Object value) {
501504
return objectToDouble(value);
502505
}
503506

504-
private static long scaleToLong(double doubleValue, double scalingFactor) {
505-
BigDecimal scaledValue = new BigDecimal(Double.toString(doubleValue)).multiply(BigDecimal.valueOf(scalingFactor));
506-
return scaledValue.setScale(0, java.math.RoundingMode.HALF_UP).longValueExact();
507-
}
508-
509-
510507
private static Double parse(XContentParser parser, boolean coerce) throws IOException {
511508
return parser.doubleValue(coerce);
512509
}

0 commit comments

Comments
 (0)