Re: BUG #14138: Inconsistent rounding behavior in float4 coercion

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: cooper(dot)charles(dot)m(at)gmail(dot)com
Cc: pgsql-bugs(at)postgresql(dot)org
Subject: Re: BUG #14138: Inconsistent rounding behavior in float4 coercion
Date: 2016-05-15 20:00:12
Message-ID: 7438.1463342412@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

cooper(dot)charles(dot)m(at)gmail(dot)com writes:
> Based on my reading of the documentation
> (http://www.postgresql.org/docs/9.5/static/datatype-numeric.html) float4
> only supports up to 6 decimal places of precision (even if the number fits
> comfortably in a 4-byte float). This is fine but I am finding it difficult
> to predict which way the float will truncate to:

I think you misunderstand floating-point arithmetic entirely. It's
rounding to the nearest so-many-digits binary value, which makes the
change in the last decimal digit hard to predict. The business about
"6 decimal digits" is only meant to indicate that you can expect that
many decimal digits to be reproduced reliably; whether and how digits
after the 6th change is a complicated business.

You can see more nearly what's going on by increasing extra_float_digits
so that more digits are printed. For example, at extra_float_digits = 3
it'll print a 9-decimal-digit rather than 6-decimal-digit approximation
to the underlying binary value, so:

regression=# select 15422.55 :: float4;
float4
------------
15422.5498
(1 row)

regression=# select 15422.5501 :: float4;
float4
------------
15422.5498
(1 row)

regression=# select 15422.5502 :: float4;
float4
------------
15422.5498
(1 row)

regression=# select 15422.5503 :: float4;
float4
------------
15422.5508
(1 row)

...

regression=# select 15422.5512 :: float4;
float4
------------
15422.5508
(1 row)

regression=# select 15422.5513 :: float4;
float4
------------
15422.5518
(1 row)

So adjacent binary values are about .001 apart in this range of values,
but they don't correspond exactly to multiples of .001. Near the
other value you cited:

regression=# select 11143.15 :: float4;
float4
------------
11143.1504
(1 row)

...

regression=# select 11143.1508 :: float4;
float4
------------
11143.1504
(1 row)

regression=# select 11143.1509 :: float4;
float4
------------
11143.1514
(1 row)

They're still about .001 apart, but now a little closer to the decimal
value below rather than the decimal value above, which accounts for the
different rounding behavior when showing fewer digits than this.

> Regardless of the default rounding mode (up / down / half even /
> you-name-it) I would expect these two to round in the same direction -- or
> at least the rounding algorithm to be specified and documented.

It's not really Postgres' business to try to document these rounding
behaviors, because as far as we're concerned they are platform-dependent.
Most modern machines follow IEEE 754,
https://en.wikipedia.org/wiki/IEEE_floating_point
but that's by no means universal and Postgres doesn't assume it.

If this is not something you're willing to deal with, use type numeric
rather than float4 or float8 --- numeric stores values in decimal notation
internally, so you don't get these odd roundoff effects from change of
base.

regards, tom lane

In response to

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message apurva.shingte 2016-05-16 04:15:12 BUG #14139: failed to load the sql modules into the database cluster
Previous Message cooper.charles.m 2016-05-15 18:37:14 BUG #14138: Inconsistent rounding behavior in float4 coercion