Commit 02ddd499 authored by Andrew Gierth's avatar Andrew Gierth

Change floating-point output format for improved performance.

Previously, floating-point output was done by rounding to a specific
decimal precision; by default, to 6 or 15 decimal digits (losing
information) or as requested using extra_float_digits. Drivers that
wanted exact float values, and applications like pg_dump that must
preserve values exactly, set extra_float_digits=3 (or sometimes 2 for
historical reasons, though this isn't enough for float4).

Unfortunately, decimal rounded output is slow enough to become a
noticable bottleneck when dealing with large result sets or COPY of
large tables when many floating-point values are involved.

Floating-point output can be done much faster when the output is not
rounded to a specific decimal length, but rather is chosen as the
shortest decimal representation that is closer to the original float
value than to any other value representable in the same precision. The
recently published Ryu algorithm by Ulf Adams is both relatively
simple and remarkably fast.

Accordingly, change float4out/float8out to output shortest decimal
representations if extra_float_digits is greater than 0, and make that
the new default. Applications that need rounded output can set
extra_float_digits back to 0 or below, and take the resulting
performance hit.

We make one concession to portability for systems with buggy
floating-point input: we do not output decimal values that fall
exactly halfway between adjacent representable binary values (which
would rely on the reader doing round-to-nearest-even correctly). This
is known to be a problem at least for VS2013 on Windows.

Our version of the Ryu code originates from
https://github.com/ulfjack/ryu/ at commit c9c3fb1979, but with the
following (significant) modifications:

 - Output format is changed to use fixed-point notation for small
   exponents, as printf would, and also to use lowercase 'e', a
   minimum of 2 exponent digits, and a mandatory sign on the exponent,
   to keep the formatting as close as possible to previous output.

 - The output of exact midpoint values is disabled as noted above.

 - The integer fast-path code is changed somewhat (since we have
   fixed-point output and the upstream did not).

 - Our project style has been largely applied to the code with the
   exception of C99 declaration-after-statement, which has been
   retained as an exception to our present policy.

 - Most of upstream's debugging and conditionals are removed, and we
   use our own configure tests to determine things like uint128
   availability.

Changing the float output format obviously affects a number of
regression tests. This patch uses an explicit setting of
extra_float_digits=0 for test output that is not expected to be
exactly reproducible (e.g. due to numerical instability or differing
algorithms for transcendental functions).

Conversions from floats to numeric are unchanged by this patch. These
may appear in index expressions and it is not yet clear whether any
change should be made, so that can be left for another day.

This patch assumes that the only supported floating point format is
now IEEE format, and the documentation is updated to reflect that.

Code by me, adapting the work of Ulf Adams and other contributors.

References:
https://dl.acm.org/citation.cfm?id=3192369

Reviewed-by: Tom Lane, Andres Freund, Donald Dong
Discussion: https://postgr.es/m/87r2el1bx6.fsf@news-spur.riddles.org.uk
parent f397e085
...@@ -732,6 +732,7 @@ CPP ...@@ -732,6 +732,7 @@ CPP
BITCODE_CXXFLAGS BITCODE_CXXFLAGS
BITCODE_CFLAGS BITCODE_CFLAGS
CFLAGS_VECTOR CFLAGS_VECTOR
PERMIT_DECLARATION_AFTER_STATEMENT
LLVM_BINPATH LLVM_BINPATH
LLVM_CXXFLAGS LLVM_CXXFLAGS
LLVM_CFLAGS LLVM_CFLAGS
...@@ -5261,6 +5262,7 @@ if test "$GCC" = yes -a "$ICC" = no; then ...@@ -5261,6 +5262,7 @@ if test "$GCC" = yes -a "$ICC" = no; then
CFLAGS="-Wall -Wmissing-prototypes -Wpointer-arith" CFLAGS="-Wall -Wmissing-prototypes -Wpointer-arith"
CXXFLAGS="-Wall -Wpointer-arith" CXXFLAGS="-Wall -Wpointer-arith"
# These work in some but not all gcc versions # These work in some but not all gcc versions
save_CFLAGS=$CFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CC} supports -Wdeclaration-after-statement, for CFLAGS" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CC} supports -Wdeclaration-after-statement, for CFLAGS" >&5
$as_echo_n "checking whether ${CC} supports -Wdeclaration-after-statement, for CFLAGS... " >&6; } $as_echo_n "checking whether ${CC} supports -Wdeclaration-after-statement, for CFLAGS... " >&6; }
...@@ -5301,7 +5303,13 @@ if test x"$pgac_cv_prog_CC_cflags__Wdeclaration_after_statement" = x"yes"; then ...@@ -5301,7 +5303,13 @@ if test x"$pgac_cv_prog_CC_cflags__Wdeclaration_after_statement" = x"yes"; then
fi fi
# -Wdeclaration-after-statement isn't applicable for C++ # -Wdeclaration-after-statement isn't applicable for C++. Specific C files
# disable it, so AC_SUBST the negative form.
PERMIT_DECLARATION_AFTER_STATEMENT=
if test x"save_$CFLAGS" != x"$CFLAGS"; then
PERMIT_DECLARATION_AFTER_STATEMENT=-Wno-declaration-after-statement
fi
# Really don't want VLAs to be used in our dialect of C # Really don't want VLAs to be used in our dialect of C
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CC} supports -Werror=vla, for CFLAGS" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CC} supports -Werror=vla, for CFLAGS" >&5
......
...@@ -476,8 +476,15 @@ if test "$GCC" = yes -a "$ICC" = no; then ...@@ -476,8 +476,15 @@ if test "$GCC" = yes -a "$ICC" = no; then
CFLAGS="-Wall -Wmissing-prototypes -Wpointer-arith" CFLAGS="-Wall -Wmissing-prototypes -Wpointer-arith"
CXXFLAGS="-Wall -Wpointer-arith" CXXFLAGS="-Wall -Wpointer-arith"
# These work in some but not all gcc versions # These work in some but not all gcc versions
save_CFLAGS=$CFLAGS
PGAC_PROG_CC_CFLAGS_OPT([-Wdeclaration-after-statement]) PGAC_PROG_CC_CFLAGS_OPT([-Wdeclaration-after-statement])
# -Wdeclaration-after-statement isn't applicable for C++ # -Wdeclaration-after-statement isn't applicable for C++. Specific C files
# disable it, so AC_SUBST the negative form.
PERMIT_DECLARATION_AFTER_STATEMENT=
if test x"save_$CFLAGS" != x"$CFLAGS"; then
PERMIT_DECLARATION_AFTER_STATEMENT=-Wno-declaration-after-statement
fi
AC_SUBST(PERMIT_DECLARATION_AFTER_STATEMENT)
# Really don't want VLAs to be used in our dialect of C # Really don't want VLAs to be used in our dialect of C
PGAC_PROG_CC_CFLAGS_OPT([-Werror=vla]) PGAC_PROG_CC_CFLAGS_OPT([-Werror=vla])
# -Wvla is not applicable for C++ # -Wvla is not applicable for C++
......
...@@ -33,11 +33,11 @@ SELECT count(*) FROM float4tmp WHERE a > -179.0; ...@@ -33,11 +33,11 @@ SELECT count(*) FROM float4tmp WHERE a > -179.0;
(1 row) (1 row)
SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
a | ?column? a | ?column?
----------+---------- ------------+-----------
-179 | 0 -179 | 0
-189.024 | 10.0239 -189.02386 | 10.023865
-158.177 | 20.8226 -158.17741 | 20.822586
(3 rows) (3 rows)
CREATE INDEX float4idx ON float4tmp USING gist ( a ); CREATE INDEX float4idx ON float4tmp USING gist ( a );
...@@ -82,10 +82,10 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; ...@@ -82,10 +82,10 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
(3 rows) (3 rows)
SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
a | ?column? a | ?column?
----------+---------- ------------+-----------
-179 | 0 -179 | 0
-189.024 | 10.0239 -189.02386 | 10.023865
-158.177 | 20.8226 -158.17741 | 20.822586
(3 rows) (3 rows)
...@@ -33,11 +33,11 @@ SELECT count(*) FROM float8tmp WHERE a > -1890.0; ...@@ -33,11 +33,11 @@ SELECT count(*) FROM float8tmp WHERE a > -1890.0;
(1 row) (1 row)
SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
a | ?column? a | ?column?
--------------+------------ --------------+--------------------
-1890 | 0 -1890 | 0
-2003.634512 | 113.634512 -2003.634512 | 113.63451200000009
-1769.73634 | 120.26366 -1769.73634 | 120.26366000000007
(3 rows) (3 rows)
CREATE INDEX float8idx ON float8tmp USING gist ( a ); CREATE INDEX float8idx ON float8tmp USING gist ( a );
...@@ -82,10 +82,10 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; ...@@ -82,10 +82,10 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
(3 rows) (3 rows)
SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
a | ?column? a | ?column?
--------------+------------ --------------+--------------------
-1890 | 0 -1890 | 0
-2003.634512 | 113.634512 -2003.634512 | 113.63451200000009
-1769.73634 | 120.26366 -1769.73634 | 120.26366000000007
(3 rows) (3 rows)
...@@ -81,21 +81,21 @@ SELECT 'NaN'::cube AS cube; ...@@ -81,21 +81,21 @@ SELECT 'NaN'::cube AS cube;
(1 row) (1 row)
SELECT '.1234567890123456'::cube AS cube; SELECT '.1234567890123456'::cube AS cube;
cube cube
--------------------- ----------------------
(0.123456789012346) (0.1234567890123456)
(1 row) (1 row)
SELECT '+.1234567890123456'::cube AS cube; SELECT '+.1234567890123456'::cube AS cube;
cube cube
--------------------- ----------------------
(0.123456789012346) (0.1234567890123456)
(1 row) (1 row)
SELECT '-.1234567890123456'::cube AS cube; SELECT '-.1234567890123456'::cube AS cube;
cube cube
---------------------- -----------------------
(-0.123456789012346) (-0.1234567890123456)
(1 row) (1 row)
-- simple lists (points) -- simple lists (points)
...@@ -943,9 +943,9 @@ SELECT cube_distance('(42,42,42,42)'::cube,'(137,137,137,137)'::cube); ...@@ -943,9 +943,9 @@ SELECT cube_distance('(42,42,42,42)'::cube,'(137,137,137,137)'::cube);
(1 row) (1 row)
SELECT cube_distance('(42,42,42)'::cube,'(137,137)'::cube); SELECT cube_distance('(42,42,42)'::cube,'(137,137)'::cube);
cube_distance cube_distance
------------------ --------------------
140.762210837994 140.76221083799445
(1 row) (1 row)
-- Test of cube function (text to cube) -- Test of cube function (text to cube)
...@@ -1356,8 +1356,9 @@ SELECT cube_size('(42,137)'::cube); ...@@ -1356,8 +1356,9 @@ SELECT cube_size('(42,137)'::cube);
0 0
(1 row) (1 row)
-- Test of distances -- Test of distances (euclidean distance may not be bit-exact)
-- --
SET extra_float_digits = 0;
SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube); SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
cube_distance cube_distance
--------------- ---------------
...@@ -1370,6 +1371,7 @@ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e; ...@@ -1370,6 +1371,7 @@ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
5 5
(1 row) (1 row)
RESET extra_float_digits;
SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube); SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
distance_chebyshev distance_chebyshev
-------------------- --------------------
...@@ -1557,6 +1559,7 @@ RESET enable_bitmapscan; ...@@ -1557,6 +1559,7 @@ RESET enable_bitmapscan;
INSERT INTO test_cube VALUES ('(1,1)'), ('(100000)'), ('(0, 100000)'); -- Some corner cases INSERT INTO test_cube VALUES ('(1,1)'), ('(100000)'), ('(0, 100000)'); -- Some corner cases
SET enable_seqscan = false; SET enable_seqscan = false;
-- Test different metrics -- Test different metrics
SET extra_float_digits = 0;
SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
c | dist c | dist
-------------------------+------------------ -------------------------+------------------
...@@ -1567,6 +1570,7 @@ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c ...@@ -1567,6 +1570,7 @@ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c
(1444, 403),(1346, 344) | 846 (1444, 403),(1346, 344) | 846
(5 rows) (5 rows)
RESET extra_float_digits;
SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
c | dist c | dist
-------------------------+------ -------------------------+------
...@@ -1751,6 +1755,7 @@ SELECT c~>(-4), c FROM test_cube ORDER BY c~>(-4) LIMIT 15; -- descending by upp ...@@ -1751,6 +1755,7 @@ SELECT c~>(-4), c FROM test_cube ORDER BY c~>(-4) LIMIT 15; -- descending by upp
-- Same queries with sequential scan (should give the same results as above) -- Same queries with sequential scan (should give the same results as above)
RESET enable_seqscan; RESET enable_seqscan;
SET enable_indexscan = OFF; SET enable_indexscan = OFF;
SET extra_float_digits = 0;
SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
c | dist c | dist
-------------------------+------------------ -------------------------+------------------
...@@ -1761,6 +1766,7 @@ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c ...@@ -1761,6 +1766,7 @@ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c
(1444, 403),(1346, 344) | 846 (1444, 403),(1346, 344) | 846
(5 rows) (5 rows)
RESET extra_float_digits;
SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
c | dist c | dist
-------------------------+------ -------------------------+------
......
...@@ -87,20 +87,20 @@ SELECT '-1e-300'::cube AS cube; ...@@ -87,20 +87,20 @@ SELECT '-1e-300'::cube AS cube;
(1 row) (1 row)
SELECT '1234567890123456'::cube AS cube; SELECT '1234567890123456'::cube AS cube;
cube cube
------------------------ -------------------------
(1.23456789012346e+15) (1.234567890123456e+15)
(1 row) (1 row)
SELECT '+1234567890123456'::cube AS cube; SELECT '+1234567890123456'::cube AS cube;
cube cube
------------------------ -------------------------
(1.23456789012346e+15) (1.234567890123456e+15)
(1 row) (1 row)
SELECT '-1234567890123456'::cube AS cube; SELECT '-1234567890123456'::cube AS cube;
cube cube
------------------------- --------------------------
(-1.23456789012346e+15) (-1.234567890123456e+15)
(1 row) (1 row)
...@@ -336,10 +336,12 @@ SELECT cube_inter('(1,2,3)'::cube, '(5,6,3)'::cube); -- point args ...@@ -336,10 +336,12 @@ SELECT cube_inter('(1,2,3)'::cube, '(5,6,3)'::cube); -- point args
SELECT cube_size('(4,8),(15,16)'::cube); SELECT cube_size('(4,8),(15,16)'::cube);
SELECT cube_size('(42,137)'::cube); SELECT cube_size('(42,137)'::cube);
-- Test of distances -- Test of distances (euclidean distance may not be bit-exact)
-- --
SET extra_float_digits = 0;
SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube); SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e; SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
RESET extra_float_digits;
SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube); SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c; SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube); SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
...@@ -395,7 +397,9 @@ INSERT INTO test_cube VALUES ('(1,1)'), ('(100000)'), ('(0, 100000)'); -- Some c ...@@ -395,7 +397,9 @@ INSERT INTO test_cube VALUES ('(1,1)'), ('(100000)'), ('(0, 100000)'); -- Some c
SET enable_seqscan = false; SET enable_seqscan = false;
-- Test different metrics -- Test different metrics
SET extra_float_digits = 0;
SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
RESET extra_float_digits;
SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
...@@ -412,7 +416,9 @@ SELECT c~>(-4), c FROM test_cube ORDER BY c~>(-4) LIMIT 15; -- descending by upp ...@@ -412,7 +416,9 @@ SELECT c~>(-4), c FROM test_cube ORDER BY c~>(-4) LIMIT 15; -- descending by upp
-- Same queries with sequential scan (should give the same results as above) -- Same queries with sequential scan (should give the same results as above)
RESET enable_seqscan; RESET enable_seqscan;
SET enable_indexscan = OFF; SET enable_indexscan = OFF;
SET extra_float_digits = 0;
SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
RESET extra_float_digits;
SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5; SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
SELECT c~>1, c FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by left bound SELECT c~>1, c FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by left bound
......
DROP INDEX trgm_idx2; DROP INDEX trgm_idx2;
\copy test_trgm3 from 'data/trgm2.data' \copy test_trgm3 from 'data/trgm2.data'
ERROR: relation "test_trgm3" does not exist ERROR: relation "test_trgm3" does not exist
-- reduce noise
set extra_float_digits = 0;
select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t;
t | sml t | sml
-------------------------------------+---------- -------------------------------------+----------
......
...@@ -10,6 +10,8 @@ WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); ...@@ -10,6 +10,8 @@ WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid);
--backslash is used in tests below, installcheck will fail if --backslash is used in tests below, installcheck will fail if
--standard_conforming_string is off --standard_conforming_string is off
set standard_conforming_strings=on; set standard_conforming_strings=on;
-- reduce noise
set extra_float_digits = 0;
select show_trgm(''); select show_trgm('');
show_trgm show_trgm
----------- -----------
......
CREATE TABLE test_trgm2(t text COLLATE "C"); CREATE TABLE test_trgm2(t text COLLATE "C");
\copy test_trgm2 from 'data/trgm2.data' \copy test_trgm2 from 'data/trgm2.data'
-- reduce noise
set extra_float_digits = 0;
select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t;
t | sml t | sml
-------------------------------------+---------- -------------------------------------+----------
......
...@@ -2,6 +2,9 @@ DROP INDEX trgm_idx2; ...@@ -2,6 +2,9 @@ DROP INDEX trgm_idx2;
\copy test_trgm3 from 'data/trgm2.data' \copy test_trgm3 from 'data/trgm2.data'
-- reduce noise
set extra_float_digits = 0;
select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t; select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <<% t order by sml desc, t;
select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t; select t,strict_word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <<% t order by sml desc, t;
select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t; select t,strict_word_similarity('Baykal',t) as sml from test_trgm2 where t %>> 'Baykal' order by sml desc, t;
......
...@@ -9,6 +9,9 @@ WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); ...@@ -9,6 +9,9 @@ WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid);
--standard_conforming_string is off --standard_conforming_string is off
set standard_conforming_strings=on; set standard_conforming_strings=on;
-- reduce noise
set extra_float_digits = 0;
select show_trgm(''); select show_trgm('');
select show_trgm('(*&^$@%@'); select show_trgm('(*&^$@%@');
select show_trgm('a b c'); select show_trgm('a b c');
......
...@@ -2,6 +2,9 @@ CREATE TABLE test_trgm2(t text COLLATE "C"); ...@@ -2,6 +2,9 @@ CREATE TABLE test_trgm2(t text COLLATE "C");
\copy test_trgm2 from 'data/trgm2.data' \copy test_trgm2 from 'data/trgm2.data'
-- reduce noise
set extra_float_digits = 0;
select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t; select t,word_similarity('Baykal',t) as sml from test_trgm2 where 'Baykal' <% t order by sml desc, t;
select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t; select t,word_similarity('Kabankala',t) as sml from test_trgm2 where 'Kabankala' <% t order by sml desc, t;
select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t; select t,word_similarity('Baykal',t) as sml from test_trgm2 where t %> 'Baykal' order by sml desc, t;
......
...@@ -1127,7 +1127,7 @@ FROM test_seg WHERE s @> '11.2..11.3' OR s IS NULL ORDER BY s; ...@@ -1127,7 +1127,7 @@ FROM test_seg WHERE s @> '11.2..11.3' OR s IS NULL ORDER BY s;
2.1 | 6.95 | 11.8 2.1 | 6.95 | 11.8
2.3 | Infinity | Infinity 2.3 | Infinity | Infinity
2.3 | Infinity | Infinity 2.3 | Infinity | Infinity
2.4 | 6.85 | 11.3 2.4 | 6.8500004 | 11.3
2.5 | 7 | 11.5 2.5 | 7 | 11.5
2.5 | 7.15 | 11.8 2.5 | 7.15 | 11.8
2.6 | Infinity | Infinity 2.6 | Infinity | Infinity
...@@ -1155,7 +1155,7 @@ FROM test_seg WHERE s @> '11.2..11.3' OR s IS NULL ORDER BY s; ...@@ -1155,7 +1155,7 @@ FROM test_seg WHERE s @> '11.2..11.3' OR s IS NULL ORDER BY s;
4.5 | 59.75 | 115 4.5 | 59.75 | 115
4.7 | 8.25 | 11.8 4.7 | 8.25 | 11.8
4.8 | 8.15 | 11.5 4.8 | 8.15 | 11.5
4.8 | 8.2 | 11.6 4.8 | 8.200001 | 11.6
4.8 | 8.65 | 12.5 4.8 | 8.65 | 12.5
4.8 | Infinity | Infinity 4.8 | Infinity | Infinity
4.9 | 8.45 | 12 4.9 | 8.45 | 12
...@@ -1244,7 +1244,7 @@ FROM test_seg WHERE s @> '11.2..11.3' OR s IS NULL ORDER BY s; ...@@ -1244,7 +1244,7 @@ FROM test_seg WHERE s @> '11.2..11.3' OR s IS NULL ORDER BY s;
9 | 10.5 | 12 9 | 10.5 | 12
9 | Infinity | Infinity 9 | Infinity | Infinity
9.2 | 10.6 | 12 9.2 | 10.6 | 12
9.4 | 10.8 | 12.2 9.4 | 10.799999 | 12.2
9.5 | 10.75 | 12 9.5 | 10.75 | 12
9.5 | 10.85 | 12.2 9.5 | 10.85 | 12.2
9.5 | Infinity | Infinity 9.5 | Infinity | Infinity
......
...@@ -7871,16 +7871,37 @@ SET XML OPTION { DOCUMENT | CONTENT }; ...@@ -7871,16 +7871,37 @@ SET XML OPTION { DOCUMENT | CONTENT };
</term> </term>
<listitem> <listitem>
<para> <para>
This parameter adjusts the number of digits displayed for This parameter adjusts the number of digits used for textual output of
floating-point values, including <type>float4</type>, <type>float8</type>, floating-point values, including <type>float4</type>, <type>float8</type>,
and geometric data types. The parameter value is added to the and geometric data types.
standard number of digits (<literal>FLT_DIG</literal> or <literal>DBL_DIG</literal>
as appropriate). The value can be set as high as 3, to include
partially-significant digits; this is especially useful for dumping
float data that needs to be restored exactly. Or it can be set
negative to suppress unwanted digits.
See also <xref linkend="datatype-float"/>.
</para> </para>
<para>
If the value is 1 (the default) or above, float values are output in
shortest-precise format; see <xref linkend="datatype-float"/>. The
actual number of digits generated depends only on the value being
output, not on the value of this parameter. At most 17 digits are
required for <type>float8</type> values, and 9 for <type>float4</type>
values. This format is both fast and precise, preserving the original
binary float value exactly when correctly read. For historical
compatibility, values up to 3 are permitted.
</para>
<para>
If the value is zero or negative, then the output is rounded to a
given decimal precision. The precision used is the standard number of
digits for the type (<literal>FLT_DIG</literal>
or <literal>DBL_DIG</literal> as appropriate) reduced according to the
value of this parameter. (For example, specifying -1 will cause float4
values to be output rounded to 5 significant digits, and float8 values
rounded to 14 digits.) This format is slower and does not preserve all
the bits of the binary float value, but may be more human-readable.
</para>
<note>
<para>
The meaning of this parameter, and its default value, changed
in <productname>PostgreSQL</productname> 12;
see <xref linkend="datatype-float"/> for further discussion.
</para>
</note>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -671,13 +671,12 @@ FROM generate_series(-3.5, 3.5, 1) as x; ...@@ -671,13 +671,12 @@ FROM generate_series(-3.5, 3.5, 1) as x;
</indexterm> </indexterm>
<para> <para>
The data types <type>real</type> and <type>double The data types <type>real</type> and <type>double precision</type> are
precision</type> are inexact, variable-precision numeric types. inexact, variable-precision numeric types. On all currently supported
In practice, these types are usually implementations of platforms, these types are implementations of <acronym>IEEE</acronym>
<acronym>IEEE</acronym> Standard 754 for Binary Floating-Point Standard 754 for Binary Floating-Point Arithmetic (single and double
Arithmetic (single and double precision, respectively), to the precision, respectively), to the extent that the underlying processor,
extent that the underlying processor, operating system, and operating system, and compiler support it.
compiler support it.
</para> </para>
<para> <para>
...@@ -715,24 +714,57 @@ FROM generate_series(-3.5, 3.5, 1) as x; ...@@ -715,24 +714,57 @@ FROM generate_series(-3.5, 3.5, 1) as x;
</para> </para>
<para> <para>
On most platforms, the <type>real</type> type has a range of at least On all currently supported platforms, the <type>real</type> type has a
1E-37 to 1E+37 with a precision of at least 6 decimal digits. The range of around 1E-37 to 1E+37 with a precision of at least 6 decimal
<type>double precision</type> type typically has a range of around digits. The <type>double precision</type> type has a range of around
1E-307 to 1E+308 with a precision of at least 15 digits. Values that 1E-307 to 1E+308 with a precision of at least 15 digits. Values that are
are too large or too small will cause an error. Rounding might too large or too small will cause an error. Rounding might take place if
take place if the precision of an input number is too high. the precision of an input number is too high. Numbers too close to zero
Numbers too close to zero that are not representable as distinct that are not representable as distinct from zero will cause an underflow
from zero will cause an underflow error. error.
</para>
<para>
By default, floating point values are output in text form in their
shortest precise decimal representation; the decimal value produced is
closer to the true stored binary value than to any other value
representable in the same binary precision. (However, the output value is
currently never <emphasis>exactly</emphasis> midway between two
representable values, in order to avoid a widespread bug where input
routines do not properly respect the round-to-even rule.) This value will
use at most 17 significant decimal digits for <type>float8</type>
values, and at most 9 digits for <type>float4</type> values.
</para> </para>
<note> <note>
<para> <para>
The <xref linkend="guc-extra-float-digits"/> setting controls the This shortest-precise output format is much faster to generate than the
number of extra significant digits included when a floating point historical rounded format.
value is converted to text for output. With the default value of </para>
<literal>0</literal>, the output is the same on every platform </note>
supported by PostgreSQL. Increasing it will produce output that
more accurately represents the stored value, but may be unportable. <para>
For compatibility with output generated by older versions
of <productname>PostgreSQL</productname>, and to allow the output
precision to be reduced, the <xref linkend="guc-extra-float-digits"/>
parameter can be used to select rounded decimal output instead. Setting a
value of 0 restores the previous default of rounding the value to 6
(for <type>float4</type>) or 15 (for <type>float8</type>)
significant decimal digits. Setting a negative value reduces the number
of digits further; for example -2 would round output to 4 or 13 digits
respectively.
</para>
<para>
Any value of <xref linkend="guc-extra-float-digits"/> greater than 0
selects the shortest-precise format.
</para>
<note>
<para>
Applications that wanted precise values have historically had to set
<xref linkend="guc-extra-float-digits"/> to 3 obtain them. For maximum
compatibility between versions, they should continue to do so.
</para> </para>
</note> </note>
...@@ -751,9 +783,7 @@ FROM generate_series(-3.5, 3.5, 1) as x; ...@@ -751,9 +783,7 @@ FROM generate_series(-3.5, 3.5, 1) as x;
</literallayout> </literallayout>
These represent the IEEE 754 special values These represent the IEEE 754 special values
<quote>infinity</quote>, <quote>negative infinity</quote>, and <quote>infinity</quote>, <quote>negative infinity</quote>, and
<quote>not-a-number</quote>, respectively. (On a machine whose <quote>not-a-number</quote>, respectively. When writing these values
floating-point arithmetic does not follow IEEE 754, these values
will probably not work as expected.) When writing these values
as constants in an SQL command, you must put quotes around them, as constants in an SQL command, you must put quotes around them,
for example <literal>UPDATE table SET x = '-Infinity'</literal>. On input, for example <literal>UPDATE table SET x = '-Infinity'</literal>. On input,
these strings are recognized in a case-insensitive manner. these strings are recognized in a case-insensitive manner.
...@@ -786,17 +816,6 @@ FROM generate_series(-3.5, 3.5, 1) as x; ...@@ -786,17 +816,6 @@ FROM generate_series(-3.5, 3.5, 1) as x;
<type>double precision</type>. <type>double precision</type>.
</para> </para>
<note>
<para>
The assumption that <type>real</type> and
<type>double precision</type> have exactly 24 and 53 bits in the
mantissa respectively is correct for IEEE-standard floating point
implementations. On non-IEEE platforms it might be off a little, but
for simplicity the same ranges of <replaceable>p</replaceable> are used
on all platforms.
</para>
</note>
</sect2> </sect2>
<sect2 id="datatype-serial"> <sect2 id="datatype-serial">
......
...@@ -261,6 +261,7 @@ CFLAGS = @CFLAGS@ ...@@ -261,6 +261,7 @@ CFLAGS = @CFLAGS@
CFLAGS_VECTOR = @CFLAGS_VECTOR@ CFLAGS_VECTOR = @CFLAGS_VECTOR@
CFLAGS_SSE42 = @CFLAGS_SSE42@ CFLAGS_SSE42 = @CFLAGS_SSE42@
CFLAGS_ARMV8_CRC32C = @CFLAGS_ARMV8_CRC32C@ CFLAGS_ARMV8_CRC32C = @CFLAGS_ARMV8_CRC32C@
PERMIT_DECLARATION_AFTER_STATEMENT = @PERMIT_DECLARATION_AFTER_STATEMENT@
CXXFLAGS = @CXXFLAGS@ CXXFLAGS = @CXXFLAGS@
LLVM_CPPFLAGS = @LLVM_CPPFLAGS@ LLVM_CPPFLAGS = @LLVM_CPPFLAGS@
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "common/int.h" #include "common/int.h"
#include "common/shortest_dec.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/array.h" #include "utils/array.h"
...@@ -30,8 +31,15 @@ ...@@ -30,8 +31,15 @@
#include "utils/timestamp.h" #include "utils/timestamp.h"
/* Configurable GUC parameter */ /*
int extra_float_digits = 0; /* Added to DBL_DIG or FLT_DIG */ * Configurable GUC parameter
*
* If >0, use shortest-decimal format for output; this is both the default and
* allows for compatibility with clients that explicitly set a value here to
* get round-trip-accurate results. If 0 or less, then use the old, slow,
* decimal rounding method.
*/
int extra_float_digits = 1;
/* Cached constants for degree-based trig functions */ /* Cached constants for degree-based trig functions */
static bool degree_consts_set = false; static bool degree_consts_set = false;
...@@ -282,6 +290,12 @@ float4out(PG_FUNCTION_ARGS) ...@@ -282,6 +290,12 @@ float4out(PG_FUNCTION_ARGS)
char *ascii = (char *) palloc(32); char *ascii = (char *) palloc(32);
int ndig = FLT_DIG + extra_float_digits; int ndig = FLT_DIG + extra_float_digits;
if (extra_float_digits > 0)
{
float_to_shortest_decimal_buf(num, ascii);
PG_RETURN_CSTRING(ascii);
}
(void) pg_strfromd(ascii, 32, ndig, num); (void) pg_strfromd(ascii, 32, ndig, num);
PG_RETURN_CSTRING(ascii); PG_RETURN_CSTRING(ascii);
} }
...@@ -498,6 +512,12 @@ float8out_internal(double num) ...@@ -498,6 +512,12 @@ float8out_internal(double num)
char *ascii = (char *) palloc(32); char *ascii = (char *) palloc(32);
int ndig = DBL_DIG + extra_float_digits; int ndig = DBL_DIG + extra_float_digits;
if (extra_float_digits > 0)
{
double_to_shortest_decimal_buf(num, ascii);
return ascii;
}
(void) pg_strfromd(ascii, 32, ndig, num); (void) pg_strfromd(ascii, 32, ndig, num);
return ascii; return ascii;
} }
......
...@@ -2667,11 +2667,12 @@ static struct config_int ConfigureNamesInt[] = ...@@ -2667,11 +2667,12 @@ static struct config_int ConfigureNamesInt[] =
{"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE, {"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the number of digits displayed for floating-point values."), gettext_noop("Sets the number of digits displayed for floating-point values."),
gettext_noop("This affects real, double precision, and geometric data types. " gettext_noop("This affects real, double precision, and geometric data types. "
"The parameter value is added to the standard number of digits " "A zero or negative parameter value is added to the standard "
"(FLT_DIG or DBL_DIG as appropriate).") "number of digits (FLT_DIG or DBL_DIG as appropriate). "
"Any value greater than zero selects precise output mode.")
}, },
&extra_float_digits, &extra_float_digits,
0, -15, 3, 1, -15, 3,
NULL, NULL, NULL NULL, NULL, NULL
}, },
......
...@@ -651,7 +651,8 @@ ...@@ -651,7 +651,8 @@
# India # India
# You can create your own file in # You can create your own file in
# share/timezonesets/. # share/timezonesets/.
#extra_float_digits = 0 # min -15, max 3 #extra_float_digits = 1 # min -15, max 3; any value >0 actually
# selects precise output mode
#client_encoding = sql_ascii # actually, defaults to database #client_encoding = sql_ascii # actually, defaults to database
# encoding # encoding
......
...@@ -44,9 +44,11 @@ override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\"" ...@@ -44,9 +44,11 @@ override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\""
override CPPFLAGS := -DFRONTEND -I. -I$(top_srcdir)/src/common $(CPPFLAGS) override CPPFLAGS := -DFRONTEND -I. -I$(top_srcdir)/src/common $(CPPFLAGS)
LIBS += $(PTHREAD_LIBS) LIBS += $(PTHREAD_LIBS)
OBJS_COMMON = base64.o config_info.o controldata_utils.o exec.o file_perm.o \ # If you add objects here, see also src/tools/msvc/Mkvcbuild.pm
ip.o keywords.o kwlookup.o link-canary.o md5.o pg_lzcompress.o \
pgfnames.o psprintf.o relpath.o \ OBJS_COMMON = base64.o config_info.o controldata_utils.o d2s.o exec.o f2s.o \
file_perm.o ip.o keywords.o kwlookup.o link-canary.o md5.o \
pg_lzcompress.o pgfnames.o psprintf.o relpath.o \
rmtree.o saslprep.o scram-common.o string.o unicode_norm.o \ rmtree.o saslprep.o scram-common.o string.o unicode_norm.o \
username.o wait_error.o username.o wait_error.o
...@@ -130,6 +132,13 @@ kwlist_d.h: $(top_srcdir)/src/include/parser/kwlist.h $(GEN_KEYWORDLIST_DEPS) ...@@ -130,6 +132,13 @@ kwlist_d.h: $(top_srcdir)/src/include/parser/kwlist.h $(GEN_KEYWORDLIST_DEPS)
# that you don't get broken parsing code, even in a non-enable-depend build. # that you don't get broken parsing code, even in a non-enable-depend build.
keywords.o keywords_shlib.o keywords_srv.o: kwlist_d.h keywords.o keywords_shlib.o keywords_srv.o: kwlist_d.h
# The code imported from Ryu gets a pass on declaration-after-statement,
# in order to keep it more closely aligned with its upstream.
RYU_FILES = d2s.o f2s.o
RYU_OBJS = $(RYU_FILES) $(RYU_FILES:%.o=%_shlib.o) $(RYU_FILES:%.o=%_srv.o)
$(RYU_OBJS): CFLAGS += $(PERMIT_DECLARATION_AFTER_STATEMENT)
# kwlist_d.h is in the distribution tarball, so it is not cleaned here. # kwlist_d.h is in the distribution tarball, so it is not cleaned here.
clean distclean: clean distclean:
rm -f libpgcommon.a libpgcommon_shlib.a libpgcommon_srv.a rm -f libpgcommon.a libpgcommon_shlib.a libpgcommon_srv.a
......
This diff is collapsed.
This diff is collapsed.
/*---------------------------------------------------------------------------
*
* Ryu floating-point output for double precision.
*
* Portions Copyright (c) 2018-2019, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/common/d2s_intrinsics.h
*
* This is a modification of code taken from github.com/ulfjack/ryu under the
* terms of the Boost license (not the Apache license). The original copyright
* notice follows:
*
* Copyright 2018 Ulf Adams
*
* The contents of this file may be used under the terms of the Apache
* License, Version 2.0.
*
* (See accompanying file LICENSE-Apache or copy at
* http://www.apache.org/licenses/LICENSE-2.0)
*
* Alternatively, the contents of this file may be used under the terms of the
* Boost Software License, Version 1.0.
*
* (See accompanying file LICENSE-Boost or copy at
* https://www.boost.org/LICENSE_1_0.txt)
*
* Unless required by applicable law or agreed to in writing, this software is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.
*
*---------------------------------------------------------------------------
*/
#ifndef RYU_D2S_INTRINSICS_H
#define RYU_D2S_INTRINSICS_H
#if defined(HAS_64_BIT_INTRINSICS)
#include <intrin.h>
static inline uint64
umul128(const uint64 a, const uint64 b, uint64 *const productHi)
{
return _umul128(a, b, productHi);
}
static inline uint64
shiftright128(const uint64 lo, const uint64 hi, const uint32 dist)
{
/*
* For the __shiftright128 intrinsic, the shift value is always modulo 64.
* In the current implementation of the double-precision version of Ryu,
* the shift value is always < 64. (In the case RYU_OPTIMIZE_SIZE == 0,
* the shift value is in the range [49, 58]. Otherwise in the range [2,
* 59].) Check this here in case a future change requires larger shift
* values. In this case this function needs to be adjusted.
*/
Assert(dist < 64);
return __shiftright128(lo, hi, (unsigned char) dist);
}
#else /* defined(HAS_64_BIT_INTRINSICS) */
static inline uint64
umul128(const uint64 a, const uint64 b, uint64 *const productHi)
{
/*
* The casts here help MSVC to avoid calls to the __allmul library
* function.
*/
const uint32 aLo = (uint32) a;
const uint32 aHi = (uint32) (a >> 32);
const uint32 bLo = (uint32) b;
const uint32 bHi = (uint32) (b >> 32);
const uint64 b00 = (uint64) aLo * bLo;
const uint64 b01 = (uint64) aLo * bHi;
const uint64 b10 = (uint64) aHi * bLo;
const uint64 b11 = (uint64) aHi * bHi;
const uint32 b00Lo = (uint32) b00;
const uint32 b00Hi = (uint32) (b00 >> 32);
const uint64 mid1 = b10 + b00Hi;
const uint32 mid1Lo = (uint32) (mid1);
const uint32 mid1Hi = (uint32) (mid1 >> 32);
const uint64 mid2 = b01 + mid1Lo;
const uint32 mid2Lo = (uint32) (mid2);
const uint32 mid2Hi = (uint32) (mid2 >> 32);
const uint64 pHi = b11 + mid1Hi + mid2Hi;
const uint64 pLo = ((uint64) mid2Lo << 32) + b00Lo;
*productHi = pHi;
return pLo;
}
static inline uint64
shiftright128(const uint64 lo, const uint64 hi, const uint32 dist)
{
/* We don't need to handle the case dist >= 64 here (see above). */
Assert(dist < 64);
#if !defined(RYU_32_BIT_PLATFORM)
Assert(dist > 0);
return (hi << (64 - dist)) | (lo >> dist);
#else
/* Avoid a 64-bit shift by taking advantage of the range of shift values. */
Assert(dist >= 32);
return (hi << (64 - dist)) | ((uint32) (lo >> 32) >> (dist - 32));
#endif
}
#endif /* // defined(HAS_64_BIT_INTRINSICS) */
#ifdef RYU_32_BIT_PLATFORM
/* Returns the high 64 bits of the 128-bit product of a and b. */
static inline uint64
umulh(const uint64 a, const uint64 b)
{
/*
* Reuse the umul128 implementation. Optimizers will likely eliminate the
* instructions used to compute the low part of the product.
*/
uint64 hi;
umul128(a, b, &hi);
return hi;
}
/*----
* On 32-bit platforms, compilers typically generate calls to library
* functions for 64-bit divisions, even if the divisor is a constant.
*
* E.g.:
* https://bugs.llvm.org/show_bug.cgi?id=37932
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=17958
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37443
*
* The functions here perform division-by-constant using multiplications
* in the same way as 64-bit compilers would do.
*
* NB:
* The multipliers and shift values are the ones generated by clang x64
* for expressions like x/5, x/10, etc.
*----
*/
static inline uint64
div5(const uint64 x)
{
return umulh(x, UINT64CONST(0xCCCCCCCCCCCCCCCD)) >> 2;
}
static inline uint64
div10(const uint64 x)
{
return umulh(x, UINT64CONST(0xCCCCCCCCCCCCCCCD)) >> 3;
}
static inline uint64
div100(const uint64 x)
{
return umulh(x >> 2, UINT64CONST(0x28F5C28F5C28F5C3)) >> 2;
}
static inline uint64
div1e8(const uint64 x)
{
return umulh(x, UINT64CONST(0xABCC77118461CEFD)) >> 26;
}
#else /* RYU_32_BIT_PLATFORM */
static inline uint64
div5(const uint64 x)
{
return x / 5;
}
static inline uint64
div10(const uint64 x)
{
return x / 10;
}
static inline uint64
div100(const uint64 x)
{
return x / 100;
}
static inline uint64
div1e8(const uint64 x)
{
return x / 100000000;
}
#endif /* RYU_32_BIT_PLATFORM */
#endif /* RYU_D2S_INTRINSICS_H */
#ifndef RYU_DIGIT_TABLE_H
#define RYU_DIGIT_TABLE_H
/*
* A table of all two-digit numbers. This is used to speed up decimal digit
* generation by copying pairs of digits into the final output.
*/
static const char DIGIT_TABLE[200] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'
};
#endif /* RYU_DIGIT_TABLE_H */
This diff is collapsed.
/*---------------------------------------------------------------------------
*
* Common routines for Ryu floating-point output.
*
* Portions Copyright (c) 2018-2019, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/common/ryu_common.h
*
* This is a modification of code taken from github.com/ulfjack/ryu under the
* terms of the Boost license (not the Apache license). The original copyright
* notice follows:
*
* Copyright 2018 Ulf Adams
*
* The contents of this file may be used under the terms of the Apache
* License, Version 2.0.
*
* (See accompanying file LICENSE-Apache or copy at
* http://www.apache.org/licenses/LICENSE-2.0)
*
* Alternatively, the contents of this file may be used under the terms of the
* Boost Software License, Version 1.0.
*
* (See accompanying file LICENSE-Boost or copy at
* https://www.boost.org/LICENSE_1_0.txt)
*
* Unless required by applicable law or agreed to in writing, this software is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.
*
*---------------------------------------------------------------------------
*/
#ifndef RYU_COMMON_H
#define RYU_COMMON_H
/*
* Upstream Ryu's output is always the shortest possible. But we adjust that
* slightly to improve portability: we avoid outputting the exact midpoint
* value between two representable floats, since that relies on the reader
* getting the round-to-even rule correct, which seems to be the common
* failure mode.
*
* Defining this to 1 would restore the upstream behavior.
*/
#define STRICTLY_SHORTEST 0
#if SIZEOF_SIZE_T < 8
#define RYU_32_BIT_PLATFORM
#endif
/* Returns e == 0 ? 1 : ceil(log_2(5^e)). */
static inline uint32
pow5bits(const int32 e)
{
/*
* This approximation works up to the point that the multiplication
* overflows at e = 3529.
*
* If the multiplication were done in 64 bits, it would fail at 5^4004
* which is just greater than 2^9297.
*/
Assert(e >= 0);
Assert(e <= 3528);
return ((((uint32) e) * 1217359) >> 19) + 1;
}
/* Returns floor(log_10(2^e)). */
static inline int32
log10Pow2(const int32 e)
{
/*
* The first value this approximation fails for is 2^1651 which is just
* greater than 10^297.
*/
Assert(e >= 0);
Assert(e <= 1650);
return (int32) ((((uint32) e) * 78913) >> 18);
}
/* Returns floor(log_10(5^e)). */
static inline int32
log10Pow5(const int32 e)
{
/*
* The first value this approximation fails for is 5^2621 which is just
* greater than 10^1832.
*/
Assert(e >= 0);
Assert(e <= 2620);
return (int32) ((((uint32) e) * 732923) >> 20);
}
static inline int
copy_special_str(char *const result, const bool sign, const bool exponent, const bool mantissa)
{
if (mantissa)
{
memcpy(result, "NaN", 3);
return 3;
}
if (sign)
{
result[0] = '-';
}
if (exponent)
{
memcpy(result + sign, "Infinity", 8);
return sign + 8;
}
result[sign] = '0';
return sign + 1;
}
static inline uint32
float_to_bits(const float f)
{
uint32 bits = 0;
memcpy(&bits, &f, sizeof(float));
return bits;
}
static inline uint64
double_to_bits(const double d)
{
uint64 bits = 0;
memcpy(&bits, &d, sizeof(double));
return bits;
}
#endif /* RYU_COMMON_H */
/*---------------------------------------------------------------------------
*
* Ryu floating-point output.
*
* Portions Copyright (c) 2018-2019, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/include/common/shortest_dec.h
*
* This is a modification of code taken from github.com/ulfjack/ryu under the
* terms of the Boost license (not the Apache license). The original copyright
* notice follows:
*
* Copyright 2018 Ulf Adams
*
* The contents of this file may be used under the terms of the Apache
* License, Version 2.0.
*
* (See accompanying file LICENSE-Apache or copy at
* http://www.apache.org/licenses/LICENSE-2.0)
*
* Alternatively, the contents of this file may be used under the terms of the
* Boost Software License, Version 1.0.
*
* (See accompanying file LICENSE-Boost or copy at
* https://www.boost.org/LICENSE_1_0.txt)
*
* Unless required by applicable law or agreed to in writing, this software is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.
*
*---------------------------------------------------------------------------
*/
#ifndef SHORTEST_DEC_H
#define SHORTEST_DEC_H
/*----
* The length of 25 comes from:
*
* Case 1: -9.9999999999999999e-299 = 24 bytes, plus 1 for null
*
* Case 2: -0.00099999999999999999 = 23 bytes, plus 1 for null
*/
#define DOUBLE_SHORTEST_DECIMAL_LEN 25
int double_to_shortest_decimal_bufn(double f, char *result);
int double_to_shortest_decimal_buf(double f, char *result);
char *double_to_shortest_decimal(double f);
/*
* The length of 16 comes from:
*
* Case 1: -9.99999999e+29 = 15 bytes, plus 1 for null
*
* Case 2: -0.000999999999 = 15 bytes, plus 1 for null
*/
#define FLOAT_SHORTEST_DECIMAL_LEN 16
int float_to_shortest_decimal_bufn(float f, char *result);
int float_to_shortest_decimal_buf(float f, char *result);
char *float_to_shortest_decimal(float f);
#endif /* SHORTEST_DEC_H */
-- --
-- AGGREGATES -- AGGREGATES
-- --
-- avoid bit-exact output here because operations may not be bit-exact.
SET extra_float_digits = 0;
SELECT avg(four) AS avg_1 FROM onek; SELECT avg(four) AS avg_1 FROM onek;
avg_1 avg_1
-------------------- --------------------
......
-- --
-- CIRCLE -- CIRCLE
-- --
-- avoid bit-exact output here because operations may not be bit-exact.
SET extra_float_digits = 0;
CREATE TABLE CIRCLE_TBL (f1 circle); CREATE TABLE CIRCLE_TBL (f1 circle);
INSERT INTO CIRCLE_TBL VALUES ('<(5,1),3>'); INSERT INTO CIRCLE_TBL VALUES ('<(5,1),3>');
INSERT INTO CIRCLE_TBL VALUES ('<(1,2),100>'); INSERT INTO CIRCLE_TBL VALUES ('<(1,2),100>');
......
This diff is collapsed.
This diff is collapsed.
...@@ -329,23 +329,23 @@ SELECT '' AS five, q1, q2, q1 / q2 AS divide, q1 % q2 AS mod FROM INT8_TBL; ...@@ -329,23 +329,23 @@ SELECT '' AS five, q1, q2, q1 / q2 AS divide, q1 % q2 AS mod FROM INT8_TBL;
(5 rows) (5 rows)
SELECT '' AS five, q1, float8(q1) FROM INT8_TBL; SELECT '' AS five, q1, float8(q1) FROM INT8_TBL;
five | q1 | float8 five | q1 | float8
------+------------------+---------------------- ------+------------------+-----------------------
| 123 | 123 | 123 | 123
| 123 | 123 | 123 | 123
| 4567890123456789 | 4.56789012345679e+15 | 4567890123456789 | 4.567890123456789e+15
| 4567890123456789 | 4.56789012345679e+15 | 4567890123456789 | 4.567890123456789e+15
| 4567890123456789 | 4.56789012345679e+15 | 4567890123456789 | 4.567890123456789e+15
(5 rows) (5 rows)
SELECT '' AS five, q2, float8(q2) FROM INT8_TBL; SELECT '' AS five, q2, float8(q2) FROM INT8_TBL;
five | q2 | float8 five | q2 | float8
------+-------------------+----------------------- ------+-------------------+------------------------
| 456 | 456 | 456 | 456
| 4567890123456789 | 4.56789012345679e+15 | 4567890123456789 | 4.567890123456789e+15
| 123 | 123 | 123 | 123
| 4567890123456789 | 4.56789012345679e+15 | 4567890123456789 | 4.567890123456789e+15
| -4567890123456789 | -4.56789012345679e+15 | -4567890123456789 | -4.567890123456789e+15
(5 rows) (5 rows)
SELECT 37 + q1 AS plus4 FROM INT8_TBL; SELECT 37 + q1 AS plus4 FROM INT8_TBL;
...@@ -726,13 +726,13 @@ SELECT CAST('42'::int2 AS int8), CAST('-37'::int2 AS int8); ...@@ -726,13 +726,13 @@ SELECT CAST('42'::int2 AS int8), CAST('-37'::int2 AS int8);
(1 row) (1 row)
SELECT CAST(q1 AS float4), CAST(q2 AS float8) FROM INT8_TBL; SELECT CAST(q1 AS float4), CAST(q2 AS float8) FROM INT8_TBL;
q1 | q2 q1 | q2
-------------+----------------------- -------------+------------------------
123 | 456 123 | 456
123 | 4.56789012345679e+15 123 | 4.567890123456789e+15
4.56789e+15 | 123 4.56789e+15 | 123
4.56789e+15 | 4.56789012345679e+15 4.56789e+15 | 4.567890123456789e+15
4.56789e+15 | -4.56789012345679e+15 4.56789e+15 | -4.567890123456789e+15
(5 rows) (5 rows)
SELECT CAST('36854775807.0'::float4 AS int8); SELECT CAST('36854775807.0'::float4 AS int8);
......
...@@ -4376,9 +4376,9 @@ select '12345.05'::jsonb::numeric; ...@@ -4376,9 +4376,9 @@ select '12345.05'::jsonb::numeric;
(1 row) (1 row)
select '12345.05'::jsonb::float4; select '12345.05'::jsonb::float4;
float4 float4
-------- ----------
12345 12345.05
(1 row) (1 row)
select '12345.05'::jsonb::float8; select '12345.05'::jsonb::float8;
......
...@@ -64,14 +64,14 @@ LINE 1: INSERT INTO LINE_TBL VALUES ('[(1,2),(1,2)]'); ...@@ -64,14 +64,14 @@ LINE 1: INSERT INTO LINE_TBL VALUES ('[(1,2),(1,2)]');
INSERT INTO LINE_TBL VALUES (line(point '(1,0)', point '(1,0)')); INSERT INTO LINE_TBL VALUES (line(point '(1,0)', point '(1,0)'));
ERROR: invalid line specification: must be two distinct points ERROR: invalid line specification: must be two distinct points
select * from LINE_TBL; select * from LINE_TBL;
s s
--------------------------------------------- ------------------------------------------------
{0,-1,5} {0,-1,5}
{1,0,5} {1,0,5}
{0,3,0} {0,3,0}
{1,-1,0} {1,-1,0}
{-0.4,-1,-6} {-0.4,-1,-6}
{-0.000184615384615385,-1,15.3846153846154} {-0.0001846153846153846,-1,15.384615384615387}
{3,NaN,5} {3,NaN,5}
{NaN,NaN,NaN} {NaN,NaN,NaN}
{0,-1,3} {0,-1,3}
......
-- --
-- POINT -- POINT
-- --
-- avoid bit-exact output here because operations may not be bit-exact.
SET extra_float_digits = 0;
CREATE TABLE POINT_TBL(f1 point); CREATE TABLE POINT_TBL(f1 point);
INSERT INTO POINT_TBL(f1) VALUES ('(0.0,0.0)'); INSERT INTO POINT_TBL(f1) VALUES ('(0.0,0.0)');
INSERT INTO POINT_TBL(f1) VALUES ('(-10.0,0.0)'); INSERT INTO POINT_TBL(f1) VALUES ('(-10.0,0.0)');
......
...@@ -910,24 +910,24 @@ insert into rtest_comp values ('p4', 'cm', 15.0); ...@@ -910,24 +910,24 @@ insert into rtest_comp values ('p4', 'cm', 15.0);
insert into rtest_comp values ('p5', 'inch', 7.0); insert into rtest_comp values ('p5', 'inch', 7.0);
insert into rtest_comp values ('p6', 'inch', 4.4); insert into rtest_comp values ('p6', 'inch', 4.4);
select * from rtest_vcomp order by part; select * from rtest_vcomp order by part;
part | size_in_cm part | size_in_cm
------+------------ ------+--------------------
p1 | 500 p1 | 500
p2 | 300 p2 | 300
p3 | 5 p3 | 5
p4 | 15 p4 | 15
p5 | 17.78 p5 | 17.78
p6 | 11.176 p6 | 11.176000000000002
(6 rows) (6 rows)
select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >; select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >;
part | size_in_cm part | size_in_cm
------+------------ ------+--------------------
p1 | 500 p1 | 500
p2 | 300 p2 | 300
p5 | 17.78 p5 | 17.78
p4 | 15 p4 | 15
p6 | 11.176 p6 | 11.176000000000002
(5 rows) (5 rows)
-- --
......
...@@ -970,9 +970,9 @@ Water, water, every where, ...@@ -970,9 +970,9 @@ Water, water, every where,
Nor any drop to drink. Nor any drop to drink.
S. T. Coleridge (1772-1834) S. T. Coleridge (1772-1834)
'), to_tsquery('english', 'breath&motion&water')); '), to_tsquery('english', 'breath&motion&water'));
ts_rank_cd ts_rank_cd
------------ -------------
0.00833333 0.008333334
(1 row) (1 row)
SELECT ts_rank_cd(to_tsvector('english', ' SELECT ts_rank_cd(to_tsvector('english', '
......
...@@ -787,57 +787,57 @@ select to_tsvector('simple', '') @@ '!foo' AS "true"; ...@@ -787,57 +787,57 @@ select to_tsvector('simple', '') @@ '!foo' AS "true";
--ranking --ranking
SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a | s');
ts_rank ts_rank
----------- -------------
0.0911891 0.091189064
(1 row) (1 row)
SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | s');
ts_rank ts_rank
----------- -------------
0.0303964 0.030396355
(1 row) (1 row)
SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | s:*'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | s:*');
ts_rank ts_rank
----------- -------------
0.0911891 0.091189064
(1 row) (1 row)
SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | sa:*'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | sa:*');
ts_rank ts_rank
----------- -------------
0.0911891 0.091189064
(1 row) (1 row)
SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a | s');
ts_rank ts_rank
---------- ------------
0.151982 0.15198177
(1 row) (1 row)
SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a | s');
ts_rank ts_rank
----------- ------------
0.0607927 0.06079271
(1 row) (1 row)
SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a & s'); SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a & s');
ts_rank ts_rank
---------- ------------
0.140153 0.14015312
(1 row) (1 row)
SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a & s'); SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a & s');
ts_rank ts_rank
---------- ------------
0.198206 0.19820644
(1 row) (1 row)
SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a & s'); SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a & s');
ts_rank ts_rank
----------- ------------
0.0991032 0.09910322
(1 row) (1 row)
SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a | s');
...@@ -885,7 +885,7 @@ SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a | s'); ...@@ -885,7 +885,7 @@ SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a | s');
SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a & s');
ts_rank_cd ts_rank_cd
------------ ------------
0.133333 0.13333334
(1 row) (1 row)
SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a & s');
...@@ -903,13 +903,13 @@ SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a & s'); ...@@ -903,13 +903,13 @@ SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a & s');
SELECT ts_rank_cd(' a:1 s:2A d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2A d g'::tsvector, 'a <-> s');
ts_rank_cd ts_rank_cd
------------ ------------
0.181818 0.18181819
(1 row) (1 row)
SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a <-> s');
ts_rank_cd ts_rank_cd
------------ ------------
0.133333 0.13333334
(1 row) (1 row)
SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a <-> s');
...@@ -927,13 +927,13 @@ SELECT ts_rank_cd(' a:1 s:2 d:2A g'::tsvector, 'a <-> s'); ...@@ -927,13 +927,13 @@ SELECT ts_rank_cd(' a:1 s:2 d:2A g'::tsvector, 'a <-> s');
SELECT ts_rank_cd(' a:1 s:2,3A d:2A g'::tsvector, 'a <2> s:A'); SELECT ts_rank_cd(' a:1 s:2,3A d:2A g'::tsvector, 'a <2> s:A');
ts_rank_cd ts_rank_cd
------------ ------------
0.0909091 0.09090909
(1 row) (1 row)
SELECT ts_rank_cd(' a:1 b:2 s:3A d:2A g'::tsvector, 'a <2> s:A'); SELECT ts_rank_cd(' a:1 b:2 s:3A d:2A g'::tsvector, 'a <2> s:A');
ts_rank_cd ts_rank_cd
------------ ------------
0.0909091 0.09090909
(1 row) (1 row)
SELECT ts_rank_cd(' a:1 sa:2D sb:2A g'::tsvector, 'a <-> s:*'); SELECT ts_rank_cd(' a:1 sa:2D sb:2A g'::tsvector, 'a <-> s:*');
......
-- --
-- UPDATABLE VIEWS -- UPDATABLE VIEWS
-- --
-- avoid bit-exact output here because operations may not be bit-exact.
SET extra_float_digits = 0;
-- check that non-updatable views and columns are rejected with useful error -- check that non-updatable views and columns are rejected with useful error
-- messages -- messages
CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified'); CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified');
......
This diff is collapsed.
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
-- AGGREGATES -- AGGREGATES
-- --
-- avoid bit-exact output here because operations may not be bit-exact.
SET extra_float_digits = 0;
SELECT avg(four) AS avg_1 FROM onek; SELECT avg(four) AS avg_1 FROM onek;
SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100; SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100;
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
-- CIRCLE -- CIRCLE
-- --
-- avoid bit-exact output here because operations may not be bit-exact.
SET extra_float_digits = 0;
CREATE TABLE CIRCLE_TBL (f1 circle); CREATE TABLE CIRCLE_TBL (f1 circle);
INSERT INTO CIRCLE_TBL VALUES ('<(5,1),3>'); INSERT INTO CIRCLE_TBL VALUES ('<(5,1),3>');
......
This diff is collapsed.
This diff is collapsed.
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
-- POINT -- POINT
-- --
-- avoid bit-exact output here because operations may not be bit-exact.
SET extra_float_digits = 0;
CREATE TABLE POINT_TBL(f1 point); CREATE TABLE POINT_TBL(f1 point);
INSERT INTO POINT_TBL(f1) VALUES ('(0.0,0.0)'); INSERT INTO POINT_TBL(f1) VALUES ('(0.0,0.0)');
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment