Skip to content

Commit

Permalink
IGNITE-23516 SQL Calcite: Fix rounding of numerics (unification for D…
Browse files Browse the repository at this point in the history
…ECIMAL and other floating point rounding) - Fixes #11644.

Signed-off-by: Aleksey Plekhanov <plehanov.alex@gmail.com>
  • Loading branch information
Vladsz83 authored and alex-plekhanov committed Nov 8, 2024
1 parent 74292a9 commit 67e2831
Show file tree
Hide file tree
Showing 5 changed files with 354 additions and 173 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import org.apache.calcite.DataContext;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.config.CalciteConnectionConfig;
Expand All @@ -38,6 +37,9 @@
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.checkerframework.checker.nullness.qual.Nullable;

import static org.apache.ignite.internal.processors.query.calcite.util.IgniteMath.NUMERIC_ROUNDING_MODE;
import static org.apache.ignite.internal.processors.query.calcite.util.IgniteMath.convertToBigDecimal;

/**
* Ignite SQL functions.
*/
Expand All @@ -48,9 +50,6 @@ public class IgniteSqlFunctions {
/** */
private static final int DFLT_NUM_PRECISION = IgniteTypeSystem.INSTANCE.getDefaultPrecision(SqlTypeName.DECIMAL);

/** */
private static final RoundingMode NUMERIC_ROUNDING_MODE = RoundingMode.HALF_UP;

/**
* Default constructor.
*/
Expand Down Expand Up @@ -145,24 +144,6 @@ public static BigDecimal toBigDecimal(Number val, int precision, int scale) {
return dec.setScale(scale, NUMERIC_ROUNDING_MODE);
}

/** */
private static BigDecimal convertToBigDecimal(Number value) {
BigDecimal dec;

if (value instanceof Float)
dec = BigDecimal.valueOf(value.floatValue());
else if (value instanceof Double)
dec = BigDecimal.valueOf(value.doubleValue());
else if (value instanceof BigDecimal)
dec = (BigDecimal)value;
else if (value instanceof BigInteger)
dec = new BigDecimal((BigInteger)value);
else
dec = new BigDecimal(value.longValue());

return dec;
}

/** Cast object depending on type to DECIMAL. */
public static BigDecimal toBigDecimal(Object o, int precision, int scale) {
if (o == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
package org.apache.ignite.internal.processors.query.calcite.util;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import org.apache.calcite.sql.type.SqlTypeName;

import static org.apache.calcite.sql.type.SqlTypeName.BIGINT;
Expand Down Expand Up @@ -45,6 +47,33 @@ public class IgniteMath {
/** */
private static final Float LOWER_LONG_FLOAT = (float)Long.MIN_VALUE;

/** */
private static final double LONG_MAX_EXT = Long.MAX_VALUE + 1.d;

/** */
private static final double LONG_MIN_EXT = Long.MIN_VALUE - 1d;

/** */
private static final double INT_MAX_EXT = Integer.MAX_VALUE + 1.d;

/** */
private static final double INT_MIN_EXT = Integer.MIN_VALUE - 1d;

/** */
private static final double SHORT_MAX_EXT = Short.MAX_VALUE + 1.d;

/** */
private static final double SHORT_MIN_EXT = Short.MIN_VALUE - 1d;

/** */
private static final double BYTE_MAX_EXT = Byte.MAX_VALUE + 1.d;

/** */
private static final double BYTE_MIN_EXT = Byte.MIN_VALUE - 1d;

/** */
public static final RoundingMode NUMERIC_ROUNDING_MODE = RoundingMode.HALF_UP;

/** Returns the sum of its arguments, throwing an exception if the result overflows an {@code long}. */
public static long addExact(long x, long y) {
long r = x + y;
Expand Down Expand Up @@ -236,37 +265,21 @@ public static byte divideExact(byte x, byte y) {
return (byte)(x / y);
}

/** */
private static void checkNumberLongBounds(SqlTypeName type, Number x) {
if (x instanceof BigDecimal) {
if ((((BigDecimal)x).compareTo(UPPER_LONG_BIG_DECIMAL) < 0 && ((BigDecimal)x).compareTo(LOWER_LONG_BIG_DECIMAL) > 0))
return;
}
else if (x instanceof Double) {
if ((((Double)x).compareTo(UPPER_LONG_DOUBLE) <= 0 && ((Double)x).compareTo(LOWER_LONG_DOUBLE) >= 0))
return;
}
else if (x instanceof Float) {
if ((((Float)x).compareTo(UPPER_LONG_FLOAT) <= 0 && ((Float)x).compareTo(LOWER_LONG_FLOAT) >= 0))
return;
}
else
return;

throw new ArithmeticException(type.getName() + " overflow");
}

/** Cast value to {@code long}, throwing an exception if the result overflows an {@code long}. */
public static long convertToLongExact(Number x) {
x = round(x);

checkNumberLongBounds(BIGINT, x);

return x.longValue();
}

/** Cast value to {@code long}, throwing an exception if the result overflows an {@code long}. */
public static long convertToLongExact(double x) {
if (x > Long.MAX_VALUE || x < Long.MIN_VALUE)
throw new ArithmeticException(BIGINT.getName() + " overflow");
x = extendToRound(x);

if (x <= LONG_MIN_EXT || x >= LONG_MAX_EXT)
throw new ArithmeticException(INTEGER.getName() + " overflow");

return (long)x;
}
Expand All @@ -283,14 +296,18 @@ public static int convertToIntExact(long x) {

/** Cast value to {@code int}, throwing an exception if the result overflows an {@code int}. */
public static int convertToIntExact(double x) {
if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE)
x = extendToRound(x);

if (x <= INT_MIN_EXT || x >= INT_MAX_EXT)
throw new ArithmeticException(INTEGER.getName() + " overflow");

return (int)x;
}

/** Cast value to {@code int}, throwing an exception if the result overflows an {@code int}. */
public static int convertToIntExact(Number x) {
x = round(x);

checkNumberLongBounds(INTEGER, x);

return convertToIntExact(x.longValue());
Expand All @@ -308,14 +325,18 @@ public static short convertToShortExact(long x) {

/** Cast value to {@code short}, throwing an exception if the result overflows an {@code short}. */
public static short convertToShortExact(double x) {
if (x > Short.MAX_VALUE || x < Short.MIN_VALUE)
x = extendToRound(x);

if (x <= SHORT_MIN_EXT || x >= SHORT_MAX_EXT)
throw new ArithmeticException(SMALLINT.getName() + " overflow");

return (short)x;
}

/** Cast value to {@code short}, throwing an exception if the result overflows an {@code short}. */
public static short convertToShortExact(Number x) {
x = round(x);

checkNumberLongBounds(SMALLINT, x);

return convertToShortExact(x.longValue());
Expand All @@ -333,16 +354,68 @@ public static byte convertToByteExact(long x) {

/** Cast value to {@code byte}, throwing an exception if the result overflows an {@code byte}. */
public static byte convertToByteExact(double x) {
if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE)
x = extendToRound(x);

if (x <= BYTE_MIN_EXT || x >= BYTE_MAX_EXT)
throw new ArithmeticException(TINYINT.getName() + " overflow");

return (byte)x;
}

/** Cast value to {@code byte}, throwing an exception if the result overflows an {@code byte}. */
public static byte convertToByteExact(Number x) {
x = round(x);

checkNumberLongBounds(TINYINT, x);

return convertToByteExact(x.longValue());
}

/** */
public static BigDecimal convertToBigDecimal(Number val) {
BigDecimal dec;

if (val instanceof Float)
dec = BigDecimal.valueOf(val.floatValue());
else if (val instanceof Double)
dec = BigDecimal.valueOf(val.doubleValue());
else if (val instanceof BigDecimal)
dec = (BigDecimal)val;
else if (val instanceof BigInteger)
dec = new BigDecimal((BigInteger)val);
else
dec = BigDecimal.valueOf(val.longValue());

return dec;
}

/** */
private static void checkNumberLongBounds(SqlTypeName type, Number x) {
if (x instanceof BigDecimal) {
if ((((BigDecimal)x).compareTo(UPPER_LONG_BIG_DECIMAL) < 0 && ((BigDecimal)x).compareTo(LOWER_LONG_BIG_DECIMAL) > 0))
return;
}
else if (x instanceof Double) {
if ((((Double)x).compareTo(UPPER_LONG_DOUBLE) <= 0 && ((Double)x).compareTo(LOWER_LONG_DOUBLE) >= 0))
return;
}
else if (x instanceof Float) {
if ((((Float)x).compareTo(UPPER_LONG_FLOAT) <= 0 && ((Float)x).compareTo(LOWER_LONG_FLOAT) >= 0))
return;
}
else
return;

throw new ArithmeticException(type.getName() + " overflow");
}

/** */
private static double extendToRound(double x) {
return x < 0.0d ? x - 0.5d : x + 0.5d;
}

/** */
private static BigDecimal round(Number x) {
return convertToBigDecimal(x).setScale(0, NUMERIC_ROUNDING_MODE);
}
}
Loading

0 comments on commit 67e2831

Please sign in to comment.