Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fractional units #27

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
37 changes: 20 additions & 17 deletions defined_units.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,30 @@ const char *base_units[N_UNITS] = {
"mol",
"cd",
"B",
"cycle",
"pixel",
};

/* Compromise: Allow the rational exponents but don't use more storage: 1, n/2, n/4 */
const struct derived_unit_t si_derived_units[] = { /* https://en.wikipedia.org/wiki/International_System_of_Units#Derived_units */
/* The second is part of this table so we can easily detect its usage for printing it using the hh:mm:ss syntax */
{ "s", { 0, 0, 1, 0, 0, 0, 0, 0,} }, /* second time s */
{ "Hz", { 0, 0, -1, 0, 0, 0, 0, 0,} }, /* hertz frequency s^-1 */
{ "N", { 1, 1, -2, 0, 0, 0, 0, 0,} }, /* newton force, weight kg·m·s^-2 */
{ "Pa", { -1, 1, -2, 0, 0, 0, 0, 0,} }, /* pascal pressure, stress N/m^2 kg·m^-1·s^-2 */
{ "J", { 2, 1, -2, 0, 0, 0, 0, 0,} }, /* joule energy, work, heat N·m kg·m^2·s^-2 */
{ "W", { 2, 1, -3, 0, 0, 0, 0, 0,} }, /* watt power, radiant flux J/s kg·m^2·s^-3 */
{ "C", { 0, 0, 1, 1, 0, 0, 0, 0,} }, /* coulomb electric charge s·A */
{ "V", { 2, 1, -3, -1, 0, 0, 0, 0,} }, /* volt voltage W/A kg·m^2·s^-3·A^-1 */
{ "F", { -2, -1, 4, 2, 0, 0, 0, 0,} }, /* farad electric capacitance C/V kg^-1·m^-2·s^4·A^2 */
{ "Ω", { 2, 1, -3, -2, 0, 0, 0, 0,} }, /* ohm electric resistance, impedance V/A kg·m^2·s^-3·A^-2 */
{ "S", { -2, -1, 3, 2, 0, 0, 0, 0,} }, /* siemens electrical conductance A/V kg^-1·m^-2·s^3·A^2 */
{ "Wb", { 2, 1, -2, -1, 0, 0, 0, 0,} }, /* weber magnetic flux V·s kg·m^2·s^-2·A^-1 */
{ "T", { 0, 1, -2, -1, 0, 0, 0, 0,} }, /* tesla magnetic flux density Wb/m^2 kg·s^-2·A^-1 */
{ "H", { 2, 1, -2, -2, 0, 0, 0, 0,} }, /* henry inductance Wb/A kg·m^2·s^-2·A^-2 */
{ "lx", { -2, 0, 0, 0, 0, 0, 1, 0,} }, /* lux illuminance lm/m^2 m^-2·cd */
{ "Gy", { 2, 0, -2, 0, 0, 0, 0, 0,} }, /* gray absorbed dose (of ionizing radiation) J/kg m^2·s^-2 */
{ "kat", { 0, 0, -1, 0, 0, 1, 0, 0,} }, /* katal catalytic activity mol·s^-1 */
{ "s", { 0*ONES_DIGIT, 0*ONES_DIGIT, 1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* second time s */
{ "Hz", { 0*ONES_DIGIT, 0*ONES_DIGIT, -1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 1*ONES_DIGIT, 0*ONES_DIGIT}, }, /* hertz frequency s^-1 */
{ "N", { 1*ONES_DIGIT, 1*ONES_DIGIT, -2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* newton force, weight kg·m·s^-2 */
{ "Pa", {-1*ONES_DIGIT, 1*ONES_DIGIT, -2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* pascal pressure, stress N/m^2 kg·m^-1·s^-2 */
{ "J", { 2*ONES_DIGIT, 1*ONES_DIGIT, -2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* joule energy, work, heat N·m kg·m^2·s^-2 */
{ "W", { 2*ONES_DIGIT, 1*ONES_DIGIT, -3*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* watt power, radiant flux J/s kg·m^2·s^-3 */
{ "C", { 0*ONES_DIGIT, 0*ONES_DIGIT, 1*ONES_DIGIT, 1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* coulomb electric charge s·A */
{ "V", { 2*ONES_DIGIT, 1*ONES_DIGIT, -3*ONES_DIGIT, -1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* volt voltage W/A kg·m^2·s^-3·A^-1 */
{ "F", {-2*ONES_DIGIT, -1*ONES_DIGIT, 4*ONES_DIGIT, 2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* farad electric capacitance C/V kg^-1·m^-2·s^4·A^2 */
{ "Ω", { 2*ONES_DIGIT, 1*ONES_DIGIT, -3*ONES_DIGIT, -2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* ohm electric resistance, impedance V/A kg·m^2·s^-3·A^-2 */
{ "S", {-2*ONES_DIGIT, -1*ONES_DIGIT, 3*ONES_DIGIT, 2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* siemens electrical conductance A/V kg^-1·m^-2·s^3·A^2 */
{ "Wb", { 2*ONES_DIGIT, 1*ONES_DIGIT, -2*ONES_DIGIT, -1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* weber magnetic flux V·s kg·m^2·s^-2·A^-1 */
{ "T", { 0*ONES_DIGIT, 1*ONES_DIGIT, -2*ONES_DIGIT, -1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* tesla magnetic flux density Wb/m^2 kg·s^-2·A^-1 */
{ "H", { 2*ONES_DIGIT, 1*ONES_DIGIT, -2*ONES_DIGIT, -2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* henry inductance Wb/A kg·m^2·s^-2·A^-2 */
{ "lx", {-2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* lux illuminance lm/m^2 m^-2·cd */
{ "Gy", { 2*ONES_DIGIT, 0*ONES_DIGIT, -2*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* gray absorbed dose (of ionizing radiation) J/kg m^2·s^-2 */
{ "kat",{ 0*ONES_DIGIT, 0*ONES_DIGIT, -1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 1*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT, 0*ONES_DIGIT}, }, /* katal catalytic activity mol·s^-1 */
{ 0 }
};

Expand Down
35 changes: 34 additions & 1 deletion unit--7.sql.in
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ CREATE FUNCTION unit_send(unit)
AS '$libdir/unit'
LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION unit_send_array(unit)
RETURNS double precision[]
AS '$libdir/unit'
LANGUAGE C IMMUTABLE STRICT;

CREATE TYPE unit (
internallength = 16,
internallength = 18,
input = unit_in,
output = unit_out,
receive = unit_recv,
Expand Down Expand Up @@ -141,6 +146,16 @@ CREATE FUNCTION byte(double precision DEFAULT 1.0)
AS '$libdir/unit', 'unit_byte'
LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION cycle(double precision DEFAULT 1.0)
RETURNS unit
AS '$libdir/unit', 'unit_cycle'
LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION pixel(double precision DEFAULT 1.0)
RETURNS unit
AS '$libdir/unit', 'unit_pixel'
LANGUAGE C IMMUTABLE STRICT;

-- functions without operators

CREATE FUNCTION value(unit)
Expand Down Expand Up @@ -311,6 +326,24 @@ CREATE FUNCTION unit_at_double(unit, text)
AS '$libdir/unit', 'unit_at_double'
LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION unit_compatible(unit, text)
RETURNS boolean
SET search_path = @extschema@
AS '$libdir/unit', 'unit_compatible'
LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION unit_smallest_pow()
RETURNS double precision
SET search_path = @extschema@
AS '$libdir/unit', 'unit_smallest_pow'
LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION unit_valid(text)
RETURNS boolean
SET search_path = @extschema@
AS '$libdir/unit', 'unit_valid'
LANGUAGE C IMMUTABLE STRICT;

CREATE OPERATOR @@ (
leftarg = unit,
rightarg = text,
Expand Down
131 changes: 122 additions & 9 deletions unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ GNU General Public License for more details.

#include "postgres.h"
#include "fmgr.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h" /* send/recv */
#include "utils/builtins.h" /* cstring_to_text (needed on 9.5) */
#include "utils/guc.h"
Expand Down Expand Up @@ -75,7 +76,7 @@ unit_get_definitions(void)
strlcpy(unit_name->name, base_units[i], UNIT_NAME_LENGTH);
unit_name->unit_shift.unit.value = 1.0;
memset(unit_name->unit_shift.unit.units, 0, N_UNITS);
unit_name->unit_shift.unit.units[i] = 1;
unit_name->unit_shift.unit.units[i] = ONES_DIGIT;
unit_name->unit_shift.shift = 0.0;
}
}
Expand Down Expand Up @@ -209,6 +210,20 @@ static char *superscripts[] = {
static void
print_exponent (char **output_p, int e)
{
/* Rational Exponent. Two least significant bits represent 1/2 and 1/4 .*/
if (ONES_DIGIT > 1 && abs(e)%ONES_DIGIT)
{
/* /2 or /4 */
*output_p += sprintf(*output_p,"^%.2f",e/((double)ONES_DIGIT));
return;
}

/* Integer Exponent */
e /= ONES_DIGIT;
if(e == 1)
{
return;
}
if (unit_output_superscript) {
char ascii_exp[5];
int i = 0;
Expand Down Expand Up @@ -571,6 +586,30 @@ unit_send(PG_FUNCTION_ARGS)
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

PG_FUNCTION_INFO_V1(unit_send_array);

Datum
unit_send_array(PG_FUNCTION_ARGS)
{
int i;
Unit *unit = (Unit *) PG_GETARG_POINTER(0);
Datum values[1 + N_UNITS];
ArrayType *result;


/* 0 element is the value */
values[0] = Float8GetDatum(unit->value);

/* Rest are double values the represent the power */
for(i = 1; i < N_UNITS + 1; ++i)
{
values[i] = Float8GetDatum((double)unit->units[i-1]/ONES_DIGIT);
}

result = construct_array(values, 1 + N_UNITS, FLOAT8OID, 8, true, 'd');
PG_RETURN_POINTER(result);
}

/* constructors */

PG_FUNCTION_INFO_V1 (dbl2unit);
Expand All @@ -594,7 +633,7 @@ unit_meter (PG_FUNCTION_ARGS)

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_m] = 1;
result->units[UNIT_m] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

Expand All @@ -607,7 +646,7 @@ unit_kilogram (PG_FUNCTION_ARGS)

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_kg] = 1;
result->units[UNIT_kg] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

Expand All @@ -620,7 +659,7 @@ unit_second (PG_FUNCTION_ARGS)

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_s] = 1;
result->units[UNIT_s] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

Expand All @@ -633,7 +672,7 @@ unit_ampere (PG_FUNCTION_ARGS)

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_A] = 1;
result->units[UNIT_A] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

Expand All @@ -646,7 +685,7 @@ unit_kelvin (PG_FUNCTION_ARGS)

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_K] = 1;
result->units[UNIT_K] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

Expand All @@ -659,7 +698,7 @@ unit_mole (PG_FUNCTION_ARGS)

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_mol] = 1;
result->units[UNIT_mol] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

Expand All @@ -672,7 +711,7 @@ unit_candela (PG_FUNCTION_ARGS)

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_cd] = 1;
result->units[UNIT_cd] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

Expand All @@ -685,7 +724,33 @@ unit_byte (PG_FUNCTION_ARGS)

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_B] = 1;
result->units[UNIT_B] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

PG_FUNCTION_INFO_V1 (unit_cycle);

Datum
unit_cycle (PG_FUNCTION_ARGS)
{
Unit *result;

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_cycle] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

PG_FUNCTION_INFO_V1 (unit_pixel);

Datum
unit_pixel (PG_FUNCTION_ARGS)
{
Unit *result;

result = (Unit *) palloc0(sizeof(Unit));
result->value = PG_GETARG_FLOAT8(0);
result->units[UNIT_pixel] = ONES_DIGIT;
PG_RETURN_POINTER(result);
}

Expand Down Expand Up @@ -1065,6 +1130,54 @@ unit_at_double(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8((a->value - bu.shift) / bu.unit.value);
}


PG_FUNCTION_INFO_V1(unit_compatible);
Datum
unit_compatible(PG_FUNCTION_ARGS)
{
Unit *a = (Unit *) PG_GETARG_POINTER(0);
char *b = text_to_cstring(PG_GETARG_TEXT_PP(1));
UnitShift bu;
if (unit_parse(b, &bu) > 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for unit: \"%s\", %s",
b, yyerrstr)));
PG_RETURN_BOOL(memcmp(a->units, bu.unit.units, N_UNITS)==0);
}

PG_FUNCTION_INFO_V1(unit_smallest_pow);
Datum
unit_smallest_pow(PG_FUNCTION_ARGS)
{
PG_RETURN_FLOAT8(1.0/ONES_DIGIT);
}


PG_FUNCTION_INFO_V1 (unit_valid);

Datum
unit_valid (PG_FUNCTION_ARGS)
{
char *str = text_to_cstring(PG_GETARG_TEXT_PP(0));
UnitShift result;
bool parse_error_p;

PG_TRY();
{
parse_error_p = unit_parse(str, &result) <= 0;
}
PG_CATCH();
{
parse_error_p = false;
FlushErrorState();
}
PG_END_TRY();

PG_RETURN_BOOL(parse_error_p);
}


/* comparisons */

static int
Expand Down
24 changes: 14 additions & 10 deletions unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@
#include <utils/hsearch.h>

/* indices */
#define UNIT_m 0 /* meter */
#define UNIT_kg 1 /* kilogram */
#define UNIT_s 2 /* second */
#define UNIT_A 3 /* ampere */
#define UNIT_K 4 /* kelvin */
#define UNIT_mol 5 /* mole */
#define UNIT_cd 6 /* candela */
#define UNIT_B 7 /* byte */

#define N_UNITS 8
#define UNIT_m 0 /* meter */
#define UNIT_kg 1 /* kilogram */
#define UNIT_s 2 /* second */
#define UNIT_A 3 /* ampere */
#define UNIT_K 4 /* kelvin */
#define UNIT_mol 5 /* mole */
#define UNIT_cd 6 /* candela */
#define UNIT_B 7 /* byte */
#define UNIT_cycle 8 /* cycle */
#define UNIT_pixel 9 /* pixel */

#define N_UNITS 10

#define ONES_DIGIT (1<<2)

/* defined units */

Expand Down
Loading