Skip to content

Commit

Permalink
sql: fix float to integer casting
Browse files Browse the repository at this point in the history
Previously, casting a float value to an integer simply truncates the float.
This commit fixes that by rounding the float value to the nearest integer,
rounding ties to even, as in postgres.

Epic: None
Fixes: #112515

Release note (bug fix): A bug has been fixed where casts of floats to integers
truncated the value. These casts now round a float to the nearest integer,
rounding ties to even integers.
  • Loading branch information
da-ket committed Jan 16, 2024
1 parent ee34958 commit 2b6e917
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 19 deletions.
3 changes: 1 addition & 2 deletions pkg/ccl/logictestccl/testdata/logic_test/udf_plpgsql
Original file line number Diff line number Diff line change
Expand Up @@ -2405,7 +2405,6 @@ subtest plpgsql_types
# If an assignment cast is available, the cast is performed automatically.
#
# float -> int
# TODO(112515): the float value is incorrectly truncated instead of rounded.
statement ok
DROP FUNCTION F();
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
Expand All @@ -2417,7 +2416,7 @@ $$ LANGUAGE PLpgSQL;
query I
SELECT f();
----
105
106

# decimal -> int
statement ok
Expand Down
24 changes: 12 additions & 12 deletions pkg/sql/colexec/colexecbase/cast.eg.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/sql/colexec/execgen/cmd/execgen/cast_gen_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ func floatToInt(intWidth, floatWidth int32) castFunc {
if math.IsNaN(float64(%[2]s)) || %[2]s <= float%[4]d(math.MinInt%[3]d) || %[2]s >= float%[4]d(math.MaxInt%[3]d) {
colexecerror.ExpectedError(tree.ErrIntOutOfRange)
}
%[1]s = int%[3]d(%[2]s)
%[1]s = int%[3]d(math.RoundToEven(%[2]s))
`
if intWidth == anyWidth {
intWidth = 64
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/logictest/testdata/logic_test/aggregate
Original file line number Diff line number Diff line change
Expand Up @@ -989,12 +989,12 @@ UPDATE mnop SET o = n::decimal, p = (n * 10)::bigint
query RRR
SELECT round(variance(n), 2), round(variance(n), 2), round(variance(p)) FROM mnop
----
0.08 0.08 8
0.08 0.08 9

query RRR
SELECT round(var_pop(n), 2), round(var_pop(n), 2), round(var_pop(p)) FROM mnop
----
0.08 0.08 8
0.08 0.08 9

query RRR
SELECT round(stddev_samp(n), 2), round(stddev(n), 2), round(stddev_samp(p)) FROM mnop
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/logictest/testdata/logic_test/target_names
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@ SELECT (SELECT 123 AS a),
(SELECT cos(0)::INT)
----
a column1 cos
123 0 1
123 1 1
2 changes: 1 addition & 1 deletion pkg/sql/sem/eval/cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func performCastWithoutPrecisionTruncation(
if math.IsNaN(f) || f <= float64(math.MinInt64) || f >= float64(math.MaxInt64) {
return nil, tree.ErrIntOutOfRange
}
res = tree.NewDInt(tree.DInt(f))
res = tree.NewDInt(tree.DInt(math.RoundToEven(f)))
case *tree.DDecimal:
i, err := roundDecimalToInt(&v.Decimal)
if err != nil {
Expand Down
22 changes: 22 additions & 0 deletions pkg/sql/sem/eval/testdata/eval/cast
Original file line number Diff line number Diff line change
Expand Up @@ -1460,3 +1460,25 @@ eval
text(1.11::float8)
----
'1.11'

# Issue #112515: float casting returns the nearest integer,
# rounding ties to even (Banker's rounding).
eval
int8(0.5::float8)
----
0

eval
int8(1.5::float8)
----
2

eval
int8(-0.5::float8)
----
0

eval
int8(-1.5::float8)
----
-2

0 comments on commit 2b6e917

Please sign in to comment.