Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/


package org.opensearch.sql.expression.operator.arthmetic;

import java.math.BigDecimal;
import java.math.RoundingMode;
import lombok.experimental.UtilityClass;

@UtilityClass

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be troublesome: opensearch-project#1045 (comment)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if MathUtil is necessary. Let's include private function truncateNumber in MathematicalFunction.java.

public class MathUtil {

/**
* Truncates a number to required decimal places.
*
* @param numberToTruncate number to be truncated
* @param numberOfDecimals required decimal places
* @return truncated number as {@link BigDecimal}
*/
public static BigDecimal truncateNumber(double numberToTruncate, int numberOfDecimals) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add unit tests with different numberOfDecimals values. The test should use another function to validate the result, for example, substring:

assertEquals(value.toString().subString(...), truncateNumber(value, scale).toString())

if (numberToTruncate > 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use a conditional instead of if...else:

return new BigDecimal(String.valueOf(numberToTruncate)).setScale(numberOfDecimals,
            numberToTruncate > 1.0 ? RoundingMode.FLOOR : RoundingMode.CEILING);

or

RoundingMode roundingMode = numberToTruncate > 1.0 ? RoundingMode.FLOOR : RoundingMode.CEILING;
    BigDecimal bd =  new BigDecimal(String.valueOf(numberToTruncate)).setScale(numberOfDecimals, roundingMode);

return new BigDecimal(String.valueOf(numberToTruncate))
.setScale(numberOfDecimals, RoundingMode.FLOOR);
} else {
return new BigDecimal(String
.valueOf(numberToTruncate)).setScale(numberOfDecimals, RoundingMode.CEILING);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -500,26 +500,22 @@ private static DefaultFunctionResolver truncate() {
FunctionDSL.impl(
FunctionDSL.nullMissingHandling(
(x, y) -> new ExprLongValue(
new BigDecimal(x.integerValue()).setScale(y.integerValue(),
RoundingMode.DOWN).longValue())),
MathUtil.truncateNumber(x.integerValue(), y.integerValue()).longValue())),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is going to convert x.integerValue() into a double since truncateNumber only accepts double. We should have a truncateNumber function for int, long, float, etc. I'm not sure this saves much effort/code?

LONG, INTEGER, INTEGER),
FunctionDSL.impl(
FunctionDSL.nullMissingHandling(
(x, y) -> new ExprLongValue(
new BigDecimal(x.integerValue()).setScale(y.integerValue(),
RoundingMode.DOWN).longValue())),
MathUtil.truncateNumber(x.longValue(), y.integerValue()).longValue())),
LONG, LONG, INTEGER),
FunctionDSL.impl(
FunctionDSL.nullMissingHandling(
(x, y) -> new ExprDoubleValue(
new BigDecimal(x.floatValue()).setScale(y.integerValue(),
RoundingMode.DOWN).doubleValue())),
MathUtil.truncateNumber(x.floatValue(), y.integerValue()).doubleValue())),
DOUBLE, FLOAT, INTEGER),
FunctionDSL.impl(
FunctionDSL.nullMissingHandling(
(x, y) -> new ExprDoubleValue(
new BigDecimal(x.doubleValue()).setScale(y.integerValue(),
RoundingMode.DOWN).doubleValue())),
MathUtil.truncateNumber(x.doubleValue(), y.integerValue()).doubleValue())),
DOUBLE, DOUBLE, INTEGER));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1721,51 +1721,51 @@ public void sqrt_missing_value() {
* Test truncate with integer value.
*/
@ParameterizedTest(name = "truncate({0}, {1})")
@ValueSource(ints = {2, -2})
@ValueSource(ints = {2, -2, Integer.MAX_VALUE, Integer.MIN_VALUE})
public void truncate_int_value(Integer value) {
FunctionExpression truncate = DSL.truncate(DSL.literal(value), DSL.literal(1));
assertThat(
truncate.valueOf(valueEnv()), allOf(hasType(LONG),
hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).longValue())));
hasValue(MathUtil.truncateNumber(value, 1).longValue())));
assertEquals(String.format("truncate(%s, 1)", value), truncate.toString());
}

/**
* Test truncate with long value.
*/
@ParameterizedTest(name = "truncate({0}, {1})")
@ValueSource(longs = {2L, -2L})
@ValueSource(longs = {2L, -2L, Long.MAX_VALUE, Long.MIN_VALUE})
public void truncate_long_value(Long value) {
FunctionExpression truncate = DSL.truncate(DSL.literal(value), DSL.literal(1));
assertThat(
truncate.valueOf(valueEnv()), allOf(hasType(LONG),
hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).longValue())));
hasValue(MathUtil.truncateNumber(value, 1).longValue())));
assertEquals(String.format("truncate(%s, 1)", value), truncate.toString());
}

/**
* Test truncate with float value.
*/
@ParameterizedTest(name = "truncate({0}, {1})")
@ValueSource(floats = {2F, -2F})
@ValueSource(floats = {2F, -2F, Float.MAX_VALUE, Float.MIN_VALUE})
public void truncate_float_value(Float value) {
FunctionExpression truncate = DSL.truncate(DSL.literal(value), DSL.literal(1));
assertThat(
truncate.valueOf(valueEnv()), allOf(hasType(DOUBLE),
hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).doubleValue())));
hasValue(MathUtil.truncateNumber(value, 1).doubleValue())));
assertEquals(String.format("truncate(%s, 1)", value), truncate.toString());
}

/**
* Test truncate with double value.
*/
@ParameterizedTest(name = "truncate({0}, {1})")
@ValueSource(doubles = {2D, -2D})
@ValueSource(doubles = {2D, -1.2D, Double.MAX_VALUE, Double.MIN_VALUE})
Comment thread
acarbonetto marked this conversation as resolved.
Outdated
public void truncate_double_value(Double value) {
FunctionExpression truncate = DSL.truncate(DSL.literal(value), DSL.literal(1));
assertThat(
truncate.valueOf(valueEnv()), allOf(hasType(DOUBLE),
hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).doubleValue())));
hasValue(MathUtil.truncateNumber(value, 1).doubleValue())));
assertEquals(String.format("truncate(%s, 1)", value), truncate.toString());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ public void testTruncate() throws IOException {
result = executeQuery("select truncate(-56, -1)");
verifySchema(result, schema("truncate(-56, -1)", null, "long"));
verifyDataRows(result, rows(-50));

result = executeQuery("select truncate(-1.2, 1)");
verifySchema(result, schema("truncate(-1.2, 1)", null, "double"));
verifyDataRows(result, rows(-1.2));

result = executeQuery("select truncate(1004.3, 1)");
verifySchema(result, schema("truncate(1004.3, 1)", null, "double"));
verifyDataRows(result, rows(1004.3));
}

@Test
Expand Down