I was wondering how Postgresql converts floating point (float4
) values to NUMERIC
.
I chose 0.1
as a testing value. This value is not accurately representable in base2, see https://float.exposed/0x3dcccccd for a visualization. So the stored value 0x3dcccccd
in hex for a float4 is actually not 0.1
but 0.100000001490116119385
.
However, I do not understand the output of the following commands:
mydb=# SELECT '0.100000001490116119385'::float4::numeric(50,50);
numeric
------------------------------------------------------
0.10000000000000000000000000000000000000000000000000
(1 row)
mydb=# SELECT '0.1'::float4::numeric(50,50);
numeric
------------------------------------------------------
0.10000000000000000000000000000000000000000000000000
mydb=# SELECT '0.10000000000000000000000000000000001'::float4::numeric(50,50);
numeric
------------------------------------------------------
0.10000000000000000000000000000000000000000000000000
Why (and how) do I get 0.1
as a result in all cases? Both, 0.1
and 0.10000000000000000000000000000000001
cannot be accurately stored in a float4. The value that can be stored is 0.100000001490116119385
which is also the closest float4 value in both cases, but that’s not what I get when casting to numeric
. Why?
2
Answers
From the source code:
Further explanation of Frank Heikens’s answer
source code idea is get the float4 input. convert to char string, then convert to numeric.
Key function is
snprintf(buf, sizeof(buf), "%.*g", FLT_DIG, val);
FLT_DIG is equal to 6.
https://pubs.opengroup.org/onlinepubs/7908799/xsh/fprintf.html
float convert to text then to numeric processs: the text after decimal delimiter can only have 6 digits precision!
snprintf example: https://legacy.cplusplus.com/reference/cstdio/snprintf/
further post: Avoid trailing zeroes in printf()