1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-19 17:02:53 +03:00

Extend int128.h to support more numeric code.

This adds a few more functions to int128.h, allowing more of numeric.c
to use 128-bit integers on all platforms.

Specifically, int64_div_fast_to_numeric() and the following aggregate
functions can now use 128-bit integers for improved performance on all
platforms, rather than just platforms with native support for int128:

- SUM(int8)
- AVG(int8)
- STDDEV_POP(int2 or int4)
- STDDEV_SAMP(int2 or int4)
- VAR_POP(int2 or int4)
- VAR_SAMP(int2 or int4)

In addition to improved performance on platforms lacking native
128-bit integer support, this significantly simplifies this numeric
code by allowing a lot of conditionally compiled code to be deleted.

A couple of numeric functions (div_var_int64() and sqrt_var()) still
contain conditionally compiled 128-bit integer code that only works on
platforms with native 128-bit integer support. Making those work more
generally would require rolling our own higher precision 128-bit
division, which isn't supported for now.

Author: Dean Rasheed <dean.a.rasheed@gmail.com>
Reviewed-by: John Naylor <johncnaylorls@gmail.com>
Discussion: https://postgr.es/m/CAEZATCWgBMc9ZwKMYqQpaQz2X6gaamYRB+RnMsUNcdMcL2Mj_w@mail.gmail.com
This commit is contained in:
Dean Rasheed
2025-08-07 15:49:24 +01:00
parent 0ef891e541
commit d699687b32
5 changed files with 486 additions and 386 deletions

View File

@@ -93,8 +93,13 @@ main(int argc, char **argv)
int64 x = pg_prng_uint64(&pg_global_prng_state);
int64 y = pg_prng_uint64(&pg_global_prng_state);
int64 z = pg_prng_uint64(&pg_global_prng_state);
int64 w = pg_prng_uint64(&pg_global_prng_state);
int32 z32 = (int32) z;
test128 t1;
test128 t2;
test128 t3;
int32 r1;
int32 r2;
/* check unsigned addition */
t1.hl.hi = x;
@@ -126,25 +131,111 @@ main(int argc, char **argv)
return 1;
}
/* check multiplication */
t1.i128 = (int128) x * (int128) y;
t2.hl.hi = t2.hl.lo = 0;
int128_add_int64_mul_int64(&t2.I128, x, y);
/* check 128-bit signed addition */
t1.hl.hi = x;
t1.hl.lo = y;
t2 = t1;
t3.hl.hi = z;
t3.hl.lo = w;
t1.i128 += t3.i128;
int128_add_int128(&t2.I128, t3.I128);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf("%016" PRIx64 " * %016" PRIx64 "\n", x, y);
printf(INT128_HEX_FORMAT " + " INT128_HEX_FORMAT "\n", x, y, z, w);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check unsigned subtraction */
t1.hl.hi = x;
t1.hl.lo = y;
t2 = t1;
t1.i128 -= (int128) (uint64) z;
int128_sub_uint64(&t2.I128, (uint64) z);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf(INT128_HEX_FORMAT " - unsigned %016" PRIx64 "\n", x, y, z);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check signed subtraction */
t1.hl.hi = x;
t1.hl.lo = y;
t2 = t1;
t1.i128 -= (int128) z;
int128_sub_int64(&t2.I128, z);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf(INT128_HEX_FORMAT " - signed %016" PRIx64 "\n", x, y, z);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check 64x64-bit multiply-add */
t1.hl.hi = x;
t1.hl.lo = y;
t2 = t1;
t1.i128 += (int128) z * (int128) w;
int128_add_int64_mul_int64(&t2.I128, z, w);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf(INT128_HEX_FORMAT " + %016" PRIx64 " * %016" PRIx64 "\n", x, y, z, w);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check 64x64-bit multiply-subtract */
t1.hl.hi = x;
t1.hl.lo = y;
t2 = t1;
t1.i128 -= (int128) z * (int128) w;
int128_sub_int64_mul_int64(&t2.I128, z, w);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf(INT128_HEX_FORMAT " - %016" PRIx64 " * %016" PRIx64 "\n", x, y, z, w);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check 128/32-bit division */
t3.hl.hi = x;
t3.hl.lo = y;
t1.i128 = t3.i128 / z32;
r1 = (int32) (t3.i128 % z32);
t2 = t3;
int128_div_mod_int32(&t2.I128, z32, &r2);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf(INT128_HEX_FORMAT " / signed %08X\n", t3.hl.hi, t3.hl.lo, z32);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
if (r1 != r2)
{
printf(INT128_HEX_FORMAT " %% signed %08X\n", t3.hl.hi, t3.hl.lo, z32);
printf("native = %08X\n", r1);
printf("result = %08X\n", r2);
return 1;
}
/* check comparison */
t1.hl.hi = x;
t1.hl.lo = y;
t2.hl.hi = z;
t2.hl.lo = pg_prng_uint64(&pg_global_prng_state);
t2.hl.lo = w;
if (my_int128_compare(t1.i128, t2.i128) !=
int128_compare(t1.I128, t2.I128))

View File

@@ -680,6 +680,25 @@ SELECT sum2(q1,q2) FROM int8_tbl;
18271560493827981
(1 row)
-- sanity checks
SELECT sum(q1+q2), sum(q1)+sum(q2) FROM int8_tbl;
sum | ?column?
-------------------+-------------------
18271560493827981 | 18271560493827981
(1 row)
SELECT sum(q1-q2), sum(q2-q1), sum(q1)-sum(q2) FROM int8_tbl;
sum | sum | ?column?
------------------+-------------------+------------------
9135780246913245 | -9135780246913245 | 9135780246913245
(1 row)
SELECT sum(q1*2000), sum(-q1*2000), 2000*sum(q1) FROM int8_tbl;
sum | sum | ?column?
----------------------+-----------------------+----------------------
27407340740741226000 | -27407340740741226000 | 27407340740741226000
(1 row)
-- test for outer-level aggregates
-- this should work
select ten, sum(distinct four) from onek a

View File

@@ -182,6 +182,11 @@ SELECT newcnt(*) AS cnt_1000 FROM onek;
SELECT oldcnt(*) AS cnt_1000 FROM onek;
SELECT sum2(q1,q2) FROM int8_tbl;
-- sanity checks
SELECT sum(q1+q2), sum(q1)+sum(q2) FROM int8_tbl;
SELECT sum(q1-q2), sum(q2-q1), sum(q1)-sum(q2) FROM int8_tbl;
SELECT sum(q1*2000), sum(-q1*2000), 2000*sum(q1) FROM int8_tbl;
-- test for outer-level aggregates
-- this should work