patternsqlMinor
changing float->numeric casts from assignment to implicit, dangerous?
Viewed 0 times
implicitnumericfloatcastsassignmentdangerouschangingfrom
Problem
In porting an application to PostgreSQL (9.1), one odd SQL incompatibility I've discovered concerns the
In MySQL,
As indicated by the docs...
http://www.postgresql.org/docs/9.1/static/functions-math.html
...Postgres has two round functions,
So, I don't understand why the two-argument form of round doesn't take a double to begin with, but whatever. In searching I discovered two solutions to this problem. One is to simply create my own version of
Now we're into hacking the catalog, and that's a sign that this might be a bad idea. But I don't have any hard evidence that it's bad.
round() function, specifically the version that takes a second argument indicating the rounding precision.In MySQL,
round(some_float_column, 2) works as expected, returning the value of some_float_column rounded to two decimal places. In Postgres it errors with ERROR: function round(double precision, integer) does not exist and suggests HINT: No function matches the given name and argument types. You might need to add explicit type casts..As indicated by the docs...
http://www.postgresql.org/docs/9.1/static/functions-math.html
...Postgres has two round functions,
round(value) which takes a double precision or a numeric, and round(value, precision) which takes a numeric only and an integer. So, I don't understand why the two-argument form of round doesn't take a double to begin with, but whatever. In searching I discovered two solutions to this problem. One is to simply create my own version of
round(value, precision) that takes (double, int) and wraps the existing (numeric, int) version with an explicit cast. That certainly works, but I don't like it (my background is in Oracle which doesn't even have a true floating point type). It seems to me that float/double ought to be implicitly castable to numeric. And it turns out that an ASSIGNMENT cast for these types comes pre-defined. But ASSIGNMENT doesn't work for function calls, as we see here, it would need to be IMPLICIT. The trouble with that is that only one cast can be defined per pair of types, and the float4->numeric and float8->numeric assignment casts are required by the system and can't be dropped. So the only way to make those casts implicit is to update pg_cast set castcontext = 'i' where castsource in (700,701) and casttarget = 1700.Now we're into hacking the catalog, and that's a sign that this might be a bad idea. But I don't have any hard evidence that it's bad.
Solution
Please be aware that different rounding algorithms are used for different data types.
https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-TABLE
When rounding values, the numeric type rounds ties away from zero,
while (on most machines) the real and double precision types round
ties to the nearest even number. For example:
https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-TABLE
When rounding values, the numeric type rounds ties away from zero,
while (on most machines) the real and double precision types round
ties to the nearest even number. For example:
SELECT x,
round(x::numeric) AS num_round,
round(x::double precision) AS dbl_round
FROM generate_series(-3.5, 3.5, 1) as x;
x | num_round | dbl_round
------+-----------+-----------
-3.5 | -4 | -4
-2.5 | -3 | -2
-1.5 | -2 | -2
-0.5 | -1 | -0
0.5 | 1 | 0
1.5 | 2 | 2
2.5 | 3 | 2
3.5 | 4 | 4Code Snippets
SELECT x,
round(x::numeric) AS num_round,
round(x::double precision) AS dbl_round
FROM generate_series(-3.5, 3.5, 1) as x;
x | num_round | dbl_round
------+-----------+-----------
-3.5 | -4 | -4
-2.5 | -3 | -2
-1.5 | -2 | -2
-0.5 | -1 | -0
0.5 | 1 | 0
1.5 | 2 | 2
2.5 | 3 | 2
3.5 | 4 | 4Context
StackExchange Database Administrators Q#21036, answer score: 6
Revisions (0)
No revisions yet.