Re: Strange results when casting string to double

From: Carsten Klein <c(dot)klein(at)datagis(dot)com>
To: pgsql-general(at)lists(dot)postgresql(dot)org
Subject: Re: Strange results when casting string to double
Date: 2022-02-20 16:00:45
Message-ID: e39fe24c-6e3e-9e8a-9024-bf1440387960@datagis.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

On 19.02.2022 20:34 Tom Lane wrote:

> Per grep, there is no call of fesetround() in the Postgres source
> tree. I'm not sure offhand whether libc exposes any other APIs
> that could change the rounding mode, but I am quite sure that we
> wouldn't be intentionally changing it anywhere.
>
> The OS would surely allow each process to have its own setting of the
> rounding mode, so I doubt you can see it from outside.
>
> Another point to keep in mind is that no matter how invasive that
> import script might be, it's still hard to explain how it'd affect
> the rounding mode in other backend processes. You have to postulate
> either that the rounding mode has been changed in the postmaster
> process (and then inherited by session backends via fork()), or that
> some code running at the time of child process creation changes the
> mode, or that they replaced numeric_float8 with something else.
>
> I think the only way that the postmaster's rounding mode could change
> after postmaster start is the cosmic-ray hypothesis; while we do have
> features that'd allow loading extra code into the postmaster, I'm
> pretty sure they only take effect at postmaster start. So even if
> that import script tried to do that, it wouldn't have succeeded yet.
>
> Of the other two hypotheses, "substitute numeric_float8" seems like
> the most likely, especially given the other stuff you mentioned the
> script doing. Have you checked the relevant pg_cast entry to see
> if it's been changed? It'd also be interesting to see if the odd
> rounding behavior happens in all databases of the cluster or just
> one.

The script has finished!

After a restart of the database, everything works as expected again.
Rounding as well as text/numeric to double precision works the same on
all of my servers.

Prior to restarting, I've implemented my own Python based versions of both

int fegetround(void);

int fesetround(int rounding_mode integer);

Have a look a these:

CREATE OR REPLACE FUNCTION fegetround()
RETURNS integer AS
$BODY$
if 'fn.fegetround' in SD:
return SD['fn.fegetround']()

from ctypes import cdll
from ctypes.util import find_library
libm = cdll.LoadLibrary(find_library('m'))
def fegetround():
return libm.fegetround()

SD['fn.fegetround'] = fegetround
return SD['fn.fegetround']()
$BODY$
LANGUAGE plpython3u VOLATILE
COST 100;

CREATE OR REPLACE FUNCTION fesetround(rounding_mode integer)
RETURNS integer AS
$BODY$
if 'fn.fesetround' in SD:
return SD['fn.fesetround'](rounding_mode)

from ctypes import cdll
from ctypes.util import find_library
libm = cdll.LoadLibrary(find_library('m'))
def fesetround(rounding_mode):
return libm.fesetround(rounding_mode)

SD['fn.fesetround'] = fesetround
return SD['fn.fesetround'](rounding_mode)
$BODY$
LANGUAGE plpython3u VOLATILE STRICT
COST 100;

With those, I was able to proof, that actually the "wrong" rounding mode

FE_DOWNWARD (0x400)

was in effect for every new process/connection with all the described
effects on casting from string or numeric to double precision:

SELECT 1.56::double precision
-> 1.55999999999999

Setting rounding mode to

FE_TONEAREST (0x0),

instantly lead back to the expected casting behavior:

SELECT 1.56::double precision
-> 1.56

Setting rounding mode after restarting the database is still possible,
however, new sessions start off with the "correct" rounding mode
FE_TONEAREST (0x0). So, the only thing that's really changed after the
restart was, that the postmaster now has the "correct" rounding mode,
which it promotes down when forking off child processes.

We'll likely never know, why ever the postmaster got tainted with that
FE_DOWNWARD (0x400) rounding mode.

As Tom Lane said, no matter how aggressive the script could be, it can,
if at all, only change its current session's rounding mode. So, maybe it
actually was a random bit flip or a side effect caused by a quite rare
error condition in postmaster.

Nearly the same is true for any core functions or casts hijacked by the
script - these are only in effect for the database the script was ever
connecting to. In my case, the script only used one database. However,
the issue was present with any database.

Two official math functions to get and set the session's rounding mode
provided by PostgreSQL could be a good add-on for any of the next
versions of the database. Thinking about it again... maybe that's just
too dangerous :-p

Finally, many thanks to all that supported me and came up with that many
helpful ideas! :-)

Regards, Carsten

In response to

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Tom Lane 2022-02-20 16:34:45 Re: Additional accessors via the Extension API ?
Previous Message liam saffioti 2022-02-20 14:33:38 Re: pg_upgrade from Postgresql-12 to Postgresql-13 fails with "Creating dump of database schemas postgres *failure*"