Re: numeric calculation bug as of 16.2-2

From: Jan Wieck <jan(at)wi3ck(dot)info>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Subject: Re: numeric calculation bug as of 16.2-2
Date: 2024-05-14 05:46:29
Message-ID: 7e9a5010-5f92-467f-82b6-c5f7e5b672cc@wi3ck.info
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

On 5/14/24 01:04, Huw Rogers wrote:
> Thanks for the reply; as you found, actually I was testing with
> (2^127-1), not 2^127, and apparently that makes the difference.
> (((2^127)-1)/(2^63))
>
> The reason I think it's a bug is that I would not expect an off-by-one
> result. I would expect some fractional error of much less than one. It's
> also suspicious that this is triggered by an all-binary-ones value.
>
> For now I'm just using a WITH INOUT cast for this, which works fine,
> although it would be easier for my purposes (adding int128 and uint128
> types via an extension) to expose numericvar_to_int128() and
> int128_to_numericvar via numeric.h so that extensions could use them.
>
> This would be the corrected test:
>
> =# select ((2^127::numeric -
> 1::numeric)/(2^63::numeric))*(2^63::numeric) = (2^127::numeric -
> 1::numeric);
>  ?column?
> ----------
>  f
> (1 row)

Your assumption that this could ever result in 'true' with every input
is still wrong as it is based on a hypothetical infinite precision.
NUMERIC never was designed for infinite precision, just arbitrary and
you didn't even specify a desired precision.

bc(1)'s output for example is

scale=100
(2^127-1)/(2^63)
18446744073709551615.9999999999999999998915797827514495565992547199130058288574218750000000000000000000000000000000000000

You have to force PostgreSQL to use that same number of digits by
invoking round():

db1=# select round((2^127::numeric - 1), 100) / (2^63::numeric);
?column?

---------------------------------------------------------------------------------------------------------------------------
18446744073709551615.9999999999999999998915797827514495565992547199130058288574218750000000000000000000000000000000000000

This has nothing to do with being on powers of two. You just noticed it
happening on those numbers. It could happen on any recurring decimal.
For example:

db1=# select (1::numeric) / (3::numeric) * (3::numeric);
?column?
------------------------
0.99999999999999999999

So your "workaround" would be something like

db1=# select round((2^127::numeric - 1), 100) / (2^63::numeric) *
(2^63::numeric);

?column?

------------------------------------------------------------------------------------------------------------------------------------
----------
170141183460469231731687303715884105727.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000

But again, it will fail on recurring decimals or even a simple construct
like sqrt(2) because no amount of digits will get those cases precise.
It only works in your particular example because (2^127-1)/(2^63) has a
finite number of decimals that is reasonable to compute to the end.

Regards, Jan

>
>
>
> On Tue, May 14, 2024 at 1:12 PM Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us
> <mailto:tgl(at)sss(dot)pgh(dot)pa(dot)us>> wrote:
>
> David Rowley <dgrowleyml(at)gmail(dot)com <mailto:dgrowleyml(at)gmail(dot)com>>
> writes:
> > On Tue, 14 May 2024 at 14:53, Huw Rogers <djnz00(at)gmail(dot)com
> <mailto:djnz00(at)gmail(dot)com>> wrote:
> >> test=# select
> ('170141183460469231731687303715884105727'::numeric /
> '9223372036854775808'::numeric) * '9223372036854775808'::numeric;
> >> ?column?
> >> -----------------------------------------
> >> 170141183460469231731687303715884105728
>
> > I don't have enough experience in NUMERIC to tell if this is a bug or
> > not.
>
> It is not.  If you think that using numeric (or any other
> general-purpose arithmetic code) means you'll always get exact answers
> for every calculation, I have a bridge in Brooklyn I'd like to sell
> you.
>
> The specific problem with the example you give is that you're using
> fractional-power-of-2 numbers and expecting them to be exactly
> representable in numeric's base-10 arithmetic.  That's not happening.
>
> Amusingly, type float8 (which is binary at bottom) can represent
> such numbers exactly, so that this works:
>
> =# select ((2^127)/(2^63))*(2^63) = (2^127);
>  ?column?
> ----------
>  t
>
> (Use pg_typeof to verify that the subexpressions are type float8.)
>
> Nonetheless, float8 has a well-deserved reputation for being imprecise
> with the decimal fractions that people commonly work with.  That's
> just the opposite side of the same coin: conversion between the two
> bases is inexact, unless you are willing to work with an unlimited
> number of fractional digits, which in practice nobody is.
>
> BTW, just as a point of order, I cannot reproduce your complaint:
>
> =# select ((2^127::numeric)/(2^63::numeric))*(2^63::numeric) =
> (2^127::numeric);
>  ?column?
> ----------
>  t
> (1 row)
>
> =# select (2^127::numeric), (2^63::numeric);
>                 ?column?                 |      ?column?
> -----------------------------------------+---------------------
>  170141183460469231731687303715884105728 | 9223372036854775808
> (1 row)
>
> =# select
> (170141183460469231731687303715884105728/9223372036854775808)*9223372036854775808 = 170141183460469231731687303715884105728;
>  ?column?
> ----------
>  t
> (1 row)
>
> I don't know where you got '170141183460469231731687303715884105727'
> from, but that seems off-by-one.  This doesn't invalidate my larger
> point though.
>
>                         regards, tom lane
>

In response to

Browse pgsql-bugs by date

  From Date Subject
Next Message Thomas Munro 2024-05-14 06:38:20 Re: [EXTERNAL] Re: Windows Application Issues | PostgreSQL | REF # 48475607
Previous Message PG Bug reporting form 2024-05-14 05:19:19 BUG #18462: Wrong SELinux types of the binary files