I have seen several questions on this topic, but the answers do not seem useful to me.
I must create a function in PostgreSQL 10.5 that returns a number with N significant digits. I have tried different solutions for this problem, however I have a problem with a particular case. Below the example, where nmNumber is the number of the input parameters and nmSf is the number of significant digits.
SELECT round(nmNumber * power(10, nmSf-1-floor(log(abs(nmNumber ))))) / power(10, nmSf-1-floor(log(abs(nmNumber)))) result1,
round(nmNumber, cast(-floor(log(abs(nmNumber))) as integer)) result2,
floor(nmNumber / (10 ^ floor(log(nmNumber) - nmSf + 1))) * (10 ^ floor(log(nmNumber) - nmSf + 1)) result3;
If nmNumber = 0.0801 and nmSf = 2 then:
result 1 = 0.08; result2 = 0.08; result 3 = 0.08
The three results are incorrect given that:
The zeros immediately after the decimal point are not significant digits.
All non-zero digits are significant.
The zeros after digits other than zero in a decimal are significant.
According to point 3, the correct result of the previous example is: 0.080 and not 0.08 and although mathematically it turns out to be the same, visually I must obtain this result. Some idea of how I can solve my problem? I suppose that returning a VARCHAR in exchange for a NUMERIC is part of the solution to be proposed.
Some idea or I'm missing something. Thank you.
This would implement what you ask for:
CREATE OR REPLACE FUNCTION f_significant_nr(_nr numeric, _sf int, OUT _nr1 text) AS
$func$
DECLARE
_sign bool; -- record negative sign
_int int; -- length of integral part
_nr_text text; -- intermediate text state
BEGIN
IF _sf < 1 THEN
RAISE EXCEPTION '_sf must be > 0!';
ELSIF _nr IS NULL THEN
RETURN; -- returns NULL
ELSIF _nr = 0 THEN
_nr1 := '0'; RETURN;
ELSIF _sf >= length(translate(_nr::text, '-.','')) THEN -- not enough digits, optional shortcut
_nr1 := _nr; RETURN;
ELSIF abs(_nr) < 1 THEN
_nr1 := COALESCE(substring(_nr::text, '^.*?[1-9]\d{' || _sf-1 || '}'), _nr::text); RETURN;
ELSIF _nr < 0 THEN -- extract neg sign
_sign := true;
_nr := _nr * -1;
END IF;
_int := trunc(log(_nr))::int + 1; -- <= 0 was excluded above!
IF _sf < _int THEN -- fractional digits not touched
_nr1 := rpad(left(_nr::text, _sf), _int, '0');
ELSE
_nr1 := trunc(_nr)::text;
IF _sf > _int AND (_nr % 1) > 0 THEN -- _sf > _int and we have significant fractional digits
_nr_text := right((_nr % 1)::text, -1); -- remainder: ".123"
_nr1 := _nr1 || COALESCE(substring(_nr_text, '^.*?[1-9]\d{' || (_sf - _int - 1)::text || '}'), _nr_text);
END IF;
END IF;
IF _sign THEN
_nr1 := '-' || _nr1;
END IF;
END
$func$ LANGUAGE plpgsql;
db<>fiddle here - with test case.
Deals with everything you throw at it, incl NULL, 0, 0.000 or negative numbers.
But I have doubts about your point 3 as commented.
postgres knows how to do that (even point 3), you just need to ask it the right way.
create or replace function
sig_fig(num numeric,prec int) returns numeric
language sql as
$$
select to_char($1, '9.'||repeat('9',$2-1)||'EEEE')::numeric
$$;
That creates a format string producing scientific
notation with a 3 digit mantissa. Uses that that
to get the number as text Then converts that back to numeric yielding a conventional number with that many significant figures.
demo:
with v as ( values (3.14159),(1234567),(0.02),(1024),(42),(0.0098765),(-6666) ) select column1 ,sig_fig(column1,3) as "3sf" from v;
column1 | 3sf
-----------+---------
3.14159 | 3.14
1234567 | 1230000
0.02 | 0.0200
1024 | 1020
42 | 42.0
0.0098765 | 0.00988
-6666 | -6670
(7 rows)
How to compute a 32 bits cyclic redundancy check (CRC-32) as a function in PostgreSQL, the same way as MySQL?
You can create the function yourself, this is a working example for PostgreSQL 9.6
CREATE OR REPLACE FUNCTION crc32(text_string text) RETURNS bigint AS $$
DECLARE
tmp bigint;
i int;
j int;
byte_length int;
binary_string bytea;
BEGIN
IF text_string = '' THEN
RETURN 0;
END IF;
i = 0;
tmp = 4294967295;
byte_length = bit_length(text_string) / 8;
binary_string = decode(replace(text_string, E'\\\\', E'\\\\\\\\'), 'escape');
LOOP
tmp = (tmp # get_byte(binary_string, i))::bigint;
i = i + 1;
j = 0;
LOOP
tmp = ((tmp >> 1) # (3988292384 * (tmp & 1)))::bigint;
j = j + 1;
IF j >= 8 THEN
EXIT;
END IF;
END LOOP;
IF i >= byte_length THEN
EXIT;
END IF;
END LOOP;
RETURN (tmp # 4294967295);
END
$$ IMMUTABLE LANGUAGE plpgsql;
Inspired from an old post with an non crc32 accepted answer. I couldn't find the original code from thinking sphinx.
create it in PG11.
text to crc32(for Little-endian)。
result is equal to java.util.zip.CRC32.
if you want crc32(t text), you can select crc32_update(0, t)
create or replace function crc32_update(crc bigint, t text) returns bigint as $$
declare
bytes bytea;
byte bigint;
len bigint;
long_mask bigint = 4294967295; -- 0xFFFFFFFF
byte_mask integer = 255; -- 0xFF
rt8_mask bigint = 16777215; -- 0xFF000000
crc_table bigint[] = array[
0, 1996959894, -301047508, -1727442502, 124634137, 1886057615, -379345611, -1637575261,
249268274, 2044508324, -522852066, -1747789432, 162941995, 2125561021, -407360249, -1866523247,
498536548, 1789927666, -205950648, -2067906082, 450548861, 1843258603, -187386543, -2083289657,
325883990, 1684777152, -43845254, -1973040660, 335633487, 1661365465, -99664541, -1928851979,
997073096, 1281953886, -715111964, -1570279054, 1006888145, 1258607687, -770865667, -1526024853,
901097722, 1119000684, -608450090, -1396901568, 853044451, 1172266101, -589951537, -1412350631,
651767980, 1373503546, -925412992, -1076862698, 565507253, 1454621731, -809855591, -1195530993,
671266974, 1594198024, -972236366, -1324619484, 795835527, 1483230225, -1050600021, -1234817731,
1994146192, 31158534, -1731059524, -271249366, 1907459465, 112637215, -1614814043, -390540237,
2013776290, 251722036, -1777751922, -519137256, 2137656763, 141376813, -1855689577, -429695999,
1802195444, 476864866, -2056965928, -228458418, 1812370925, 453092731, -2113342271, -183516073,
1706088902, 314042704, -1950435094, -54949764, 1658658271, 366619977, -1932296973, -69972891,
1303535960, 984961486, -1547960204, -725929758, 1256170817, 1037604311, -1529756563, -740887301,
1131014506, 879679996, -1385723834, -631195440, 1141124467, 855842277, -1442165665, -586318647,
1342533948, 654459306, -1106571248, -921952122, 1466479909, 544179635, -1184443383, -832445281,
1591671054, 702138776, -1328506846, -942167884, 1504918807, 783551873, -1212326853, -1061524307,
-306674912, -1698712650, 62317068, 1957810842, -355121351, -1647151185, 81470997, 1943803523,
-480048366, -1805370492, 225274430, 2053790376, -468791541, -1828061283, 167816743, 2097651377,
-267414716, -2029476910, 503444072, 1762050814, -144550051, -2140837941, 426522225, 1852507879,
-19653770, -1982649376, 282753626, 1742555852, -105259153, -1900089351, 397917763, 1622183637,
-690576408, -1580100738, 953729732, 1340076626, -776247311, -1497606297, 1068828381, 1219638859,
-670225446, -1358292148, 906185462, 1090812512, -547295293, -1469587627, 829329135, 1181335161,
-882789492, -1134132454, 628085408, 1382605366, -871598187, -1156888829, 570562233, 1426400815,
-977650754, -1296233688, 733239954, 1555261956, -1026031705, -1244606671, 752459403, 1541320221,
-1687895376, -328994266, 1969922972, 40735498, -1677130071, -351390145, 1913087877, 83908371,
-1782625662, -491226604, 2075208622, 213261112, -1831694693, -438977011, 2094854071, 198958881,
-2032938284, -237706686, 1759359992, 534414190, -2118248755, -155638181, 1873836001, 414664567,
-2012718362, -15766928, 1711684554, 285281116, -1889165569, -127750551, 1634467795, 376229701,
-1609899400, -686959890, 1308918612, 956543938, -1486412191, -799009033, 1231636301, 1047427035,
-1362007478, -640263460, 1088359270, 936918000, -1447252397, -558129467, 1202900863, 817233897,
-1111625188, -893730166, 1404277552, 615818150, -1160759803, -841546093, 1423857449, 601450431,
-1285129682, -1000256840, 1567103746, 711928724, -1274298825, -1022587231, 1510334235, 755167117
];
begin
crc = ~crc;
len = bit_length(t) / 8;
bytes = decode(t, 'escape');
for i in 0..len-1 loop
byte = (get_byte(bytes, i))::bigint;
crc = (crc >> 8 & rt8_mask) # crc_table[((crc # byte) & byte_mask) + 1];
end loop;
crc = ~crc;
return crc & long_mask;
end
$$ immutable language plpgsql
If you want a checksum of the results which does not need to be crc32, you can use md5:
select sum(('x' || lpad(substring(md5(mycolumn), 27, 6), 8, '0'))::bit(32)::int) from mytable
md5 returns hex (128 bits). I use the final 6 characters of the md5 so it does not overflow an int. If the sum overflows an int, it may error. This should give a rudimentary checksum of the results.
SELECT 'xffffffff'::bit(32)::int;
> -1
SELECT 'x7fffffff'::bit(32)::int + 1;
ERROR: integer out of range
SELECT 'x7fffffffffffffff'::bit(64)::bigint + 1;
ERROR: bigint out of range