-
Notifications
You must be signed in to change notification settings - Fork 546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add precision parameter to round function #13965
base: master
Are you sure you want to change the base?
Conversation
e6c7ffc
to
b609549
Compare
What is the reason for this deviation? |
In PostgreSQL: In CrateDB:
PostgreSQL15 > SELECT
pg_typeof(round(1::INT)),
pg_typeof(round(1.0::FLOAT4)),
pg_typeof(round(1.0::FLOAT8)),
pg_typeof(round(1::INT,0)),
pg_typeof(round(1.1::NUMERIC,0));
pg_typeof | pg_typeof | pg_typeof | pg_typeof | pg_typeof
------------------+------------------+------------------+-----------+-----------
double precision | double precision | double precision | numeric | numeric
(1 row)
-----------
> SELECT
pg_typeof(trunc(1::INT)),
pg_typeof(trunc(1.0::FLOAT4)),
pg_typeof(trunc(1.0::FLOAT8)),
pg_typeof(trunc(1::INT,0)),
pg_typeof(trunc(1.1::NUMERIC,0));
pg_typeof | pg_typeof | pg_typeof | pg_typeof | pg_typeof
------------------+------------------+------------------+-----------+-----------
double precision | double precision | double precision | numeric | numeric
(1 row)
-----------
> SELECT pg_typeof(round(1.0::FLOAT8,0));
No function matches the given name and argument types. You might need to add explicit type casts.
-----------
> SELECT pg_typeof(round(1.0::FLOAT8::NUMERIC,0));
pg_typeof
-----------
numeric
(1 row) CrateDB 5.4.0 (PR) cr> SELECT pg_typeof(round(1::INT)),
pg_typeof(round(1.0::FLOAT4)),
pg_typeof(round(1.0::FLOAT8)),
pg_typeof(round(1::INT,0)),
pg_typeof(round(1.1::NUMERIC,0));
+-----------+-----------+----------+--------------------+--------------------+
| 'integer' | 'integer' | 'bigint' | 'double precision' | 'double precision' |
+-----------+-----------+----------+--------------------+--------------------+
| integer | integer | bigint | double precision | double precision |
+-----------+-----------+----------+--------------------+--------------------+
SELECT 1 row in set (0.041 sec)
cr> SELECT
pg_typeof(trunc(1::INT)),
pg_typeof(trunc(1.0::FLOAT4)),
pg_typeof(trunc(1.0::FLOAT8)),
pg_typeof(trunc(1::INT,0)),
pg_typeof(trunc(1.1::NUMERIC,0));
+-----------+-----------+----------+--------------------+--------------------+
| 'integer' | 'integer' | 'bigint' | 'double precision' | 'double precision' |
+-----------+-----------+----------+--------------------+--------------------+
| integer | integer | bigint | double precision | double precision |
+-----------+-----------+----------+--------------------+--------------------+
SELECT 1 row in set (0.005 sec) |
server/src/main/java/io/crate/expression/scalar/arithmetic/RoundFunction.java
Outdated
Show resolved
Hide resolved
} | ||
double val = n.doubleValue(); | ||
int numDecimals = nd.intValue(); | ||
return BigDecimal.valueOf(val).setScale(numDecimals, RoundingMode.HALF_UP).doubleValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if it's not overflowing, (round(<val> * 10 ^ numDecimals)) / <val> * 10 ^ numDecimals)
would be faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am on the fence if this might not better return a NUMERIC
.
I would think that the primary use cases for this function is some sort of data presentation / reporting use case, where one would want to limit the amount digits in the fractional part. Using a floating point value, doesn't really guarantee this.
wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if it's not overflowing, (round( * 10 ^ numDecimals)) / * 10 ^ numDecimals) would be faster.
There is both the problem of overflowing and precision loss as e.g. pointed out here. While one could check overflow and precision to fit within the max safe integer bounds of a double, the checks become cumbersome and harder to maintain. As this would be primarily be a perform optimisation and I'd expect this function to be mostly used for presentation purposes after calculation on a final result set, I think it isn't worth it. Therefore I would suggest to stay with the BigDecimal approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I slightly agree on using NUMERIC
instead, any objections? @mfussenegger
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if it's not overflowing, (round( * 10 ^ numDecimals)) / * 10 ^ numDecimals) would be faster.
There is both the problem of overflowing and precision loss as e.g. pointed out here. While one could check overflow and precision to fit within the max safe integer bounds of a double, the checks become cumbersome and harder to maintain. As this would be primarily be a perform optimisation and I'd expect this function to be mostly used for presentation purposes after calculation on a final result set, I think it isn't worth it. Therefore I would suggest to stay with the BigDecimal approach.
Is detecting a possible overflow really that complex?
I do not completely follow you argumentation about the actual use-cases of this function as we never know for sure how users are using it.
But I'm also fine on using your approach now, we can follow up here anytime without a user-facing change
b609549
to
10da948
Compare
46fa828
to
d3f4479
Compare
server/src/main/java/io/crate/expression/scalar/arithmetic/RoundFunction.java
Show resolved
Hide resolved
} | ||
double val = n.doubleValue(); | ||
int numDecimals = nd.intValue(); | ||
return BigDecimal.valueOf(val).setScale(numDecimals, RoundingMode.HALF_UP).doubleValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I slightly agree on using NUMERIC
instead, any objections? @mfussenegger
} | ||
double val = n.doubleValue(); | ||
int numDecimals = nd.intValue(); | ||
return BigDecimal.valueOf(val).setScale(numDecimals, RoundingMode.HALF_UP).doubleValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if it's not overflowing, (round( * 10 ^ numDecimals)) / * 10 ^ numDecimals) would be faster.
There is both the problem of overflowing and precision loss as e.g. pointed out here. While one could check overflow and precision to fit within the max safe integer bounds of a double, the checks become cumbersome and harder to maintain. As this would be primarily be a perform optimisation and I'd expect this function to be mostly used for presentation purposes after calculation on a final result set, I think it isn't worth it. Therefore I would suggest to stay with the BigDecimal approach.
Is detecting a possible overflow really that complex?
I do not completely follow you argumentation about the actual use-cases of this function as we never know for sure how users are using it.
But I'm also fine on using your approach now, we can follow up here anytime without a user-facing change
d3f4479
to
70103db
Compare
This would be needed for some tests in Tableau |
70103db
to
681065a
Compare
681065a
to
7f8a4d9
Compare
I think the only real open point is the question about the return type. Otherwise this PR is sitting ready for a while now and could be merged. |
Summary of the changes / Why this improves CrateDB
Enhanced the
round()
scalar function to allow a second input parameter for rounding precision.When it is specified, the result's type is
double precision
. Notice thatround(number)
andround(number, 0)
return different result types.Implementation deviates from PostgreSQL. PostgreSQL always returns
NUMERIC
forround()
.Is coherent with
trunc()
scalar function.closes #13964
Checklist
CHANGES.txt
for user facing changessql_features
table for user facing changesCHANGES.txt
(E.g. AdminUI)