Skip to content

Commit

Permalink
feat: add decimal argument support to round function
Browse files Browse the repository at this point in the history
The round function has a number of variants to support different numeric types.  This commit adds support for rounding decimals.

The precision and scale of the resultant decimal type is
calculated from the input decimal parameters and the scale
argument according to the logic in the Spark implementation:
https://github.com/apache/spark/blob/master/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/mathExpressions.scala#L1492

Signed-off-by: Andrew Coleman <[email protected]>
  • Loading branch information
andrew-coleman committed Feb 6, 2025
1 parent 5932eb9 commit 1dbdb4b
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
80 changes: 80 additions & 0 deletions extensions/functions_rounding_decimal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
%YAML 1.2
---
scalar_functions:
-
name: "ceil"
description: >
Rounding to the ceiling of the value `x`.
impls:
- args:
- value: decimal<P,S>
name: x
return: |-
integral_least_num_digits = P - S + 1
precision = min(integral_least_num_digits, 38)
decimal?<precision, 0>
-
name: "floor"
description: >
Rounding to the floor of the value `x`.
impls:
- args:
- value: decimal<P,S>
name: x
return: |-
integral_least_num_digits = P - S + 1
precision = min(integral_least_num_digits, 38)
decimal?<precision, 0>
-
name: "round"
description: >
Rounding the value `x` to `s` decimal places.
impls:
- args:
- value: decimal<P1,S1>
name: x
description: >
Numerical expression to be rounded.
- value: i32
name: s
description: >
Number of decimal places to be rounded to.
When `s` is a positive number, the rounding
is performed to a `s` number of decimal places.
When `s` is a negative number, the rounding is
performed to the left side of the decimal point
as specified by `s`.
The precision and scale of the resultant decimal
type will be adjusted from the input decimal type
dependent on the value of `s`.
options:
rounding:
description: >
When a boundary is computed to lie somewhere between two values,
and this value cannot be exactly represented, this specifies how
to round it.
- TIE_TO_EVEN: round to nearest value; if exactly halfway, tie
to the even option.
- TIE_AWAY_FROM_ZERO: round to nearest value; if exactly
halfway, tie away from zero.
- TRUNCATE: always round toward zero.
- CEILING: always round toward positive infinity.
- FLOOR: always round toward negative infinity.
- AWAY_FROM_ZERO: round negative values with FLOOR rule, round positive values with CEILING rule
- TIE_DOWN: round ties with FLOOR rule
- TIE_UP: round ties with CEILING rule
- TIE_TOWARDS_ZERO: round ties with TRUNCATE rule
- TIE_TO_ODD: round to nearest value; if exactly halfway, tie
to the odd option.
values: [ TIE_TO_EVEN, TIE_AWAY_FROM_ZERO, TRUNCATE, CEILING, FLOOR,
AWAY_FROM_ZERO, TIE_DOWN, TIE_UP, TIE_TOWARDS_ZERO, TIE_TO_ODD ]
nullability: DECLARED_OUTPUT
return: |-
integral_digits = P1 - S1 + 1
scale = s > 0 ? min(s, S1) : 0
precision = s > 0 ? min(integral_digits + scale, 38) : min(max(integral_num_digits, 1- s), 38)
decimal?<precision, scale>
7 changes: 7 additions & 0 deletions tests/cases/rounding_decimal/ceil_decimal.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### SUBSTRAIT_SCALAR_TEST: v1.0
### SUBSTRAIT_INCLUDE: '/extensions/functions_rounding_decimal.yaml'

# basic: Basic examples without any special cases
ceil(2.25::dec<8,2>) = 3::dec<7,0>
ceil(-65.5::dec<8,2>) = -65::dec<7,0>
ceil(9.9::dec<2,1>) = 10::dec<2,0>
6 changes: 6 additions & 0 deletions tests/cases/rounding_decimal/floor_decimal.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### SUBSTRAIT_SCALAR_TEST: v1.0
### SUBSTRAIT_INCLUDE: '/extensions/functions_rounding_decimal.yaml'

# basic: Basic examples without any special cases
floor(2.25::dec<8,2>) = 2::dec<7,0>
floor(-65.5::dec<8,2>) = -66::dec<7,0>
11 changes: 11 additions & 0 deletions tests/cases/rounding_decimal/round_decimal.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
### SUBSTRAIT_SCALAR_TEST: v1.0
### SUBSTRAIT_INCLUDE: '/extensions/functions_rounding_decimal.yaml'

# basic: Basic examples without any special cases
round(2::dec<2,0>, 2::i32) = 2::dec<3,0>
round(2.75::dec<8,2>, 1::i32) = 2.8::dec<8,1>

# negative_rounding: Examples with negative rounding
round(2::dec<2,0>, -2::i32) = 0::dec<2,0>
round(123::dec<2,0>, -2::i32) = 100::dec<2,0>
round(8793::dec<2,0>, -2::i32) = 8800::dec<2,0>

0 comments on commit 1dbdb4b

Please sign in to comment.