feq (and friends) are an extension I wrote that does proper floating point
comparisons:
/*
** 2015-11-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This SQLite extension implements functions for comparison of floating point
** double precision numbers x and y where x is within a specified number of
** ULPs of y.
**
** The default number of ULPs if not specified is 5.
** The size of an ULP is based on the scale of x.
** If only one value is specified it will be a y value and x will be 0.0
**
** ulp(x) -> ULP precision of x (increment to next representable number)
** epsilon(x) -> ULP precision of x (alias of ulp)
** ulps(x, y) -> number of ULP of x in (x - y)
**
** flt(x[, y[, u]]) -> x less than y
** fle(x[, y[, u]]) -> x less or equal y
** feq(x[, y[, u]]) -> x equal y
** fge(x[, y[, u]]) -> x greater or equal y
** fgt(x[, y[, u]]) -> x greater than y
** fne(x[, y[, u]]) -> x not equal y
**
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
** If the MATH.H is already included, use the math library
** otherwise make sure memcpy is defined
*/
#ifndef _MATH_H_
#include <string.h>
#endif
#ifndef SQLITE_PRIVATE
#define SQLITE_PRIVATE static
#endif
#ifdef SQLITE_CORE
#include "sqlite3.h"
#else
#ifdef _HAVE_SQLITE_CONFIG_H
#include "config.h"
#endif
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#endif
/*
** Common Functions
*/
#ifdef _MATH_H_
/*
** If we have the math library then use it
*/
static double epsilon(double value)
{
int exponent;
double mantissa = frexp(value, &exponent);
return ldexp(1.0, exponent - 53);
}
#define sqlfcmp_fabs fabs
#else
/*
** If we do not have the math library already, do not force it to be included
*/
static double epsilon(double value)
{
sqlite_int64 a;
double r = value;
if (r < 0)
r = -r;
memcpy(&a, &r, sizeof(a));
if (a == 0)
a = (970LL << 52);
else
a = ((a >> 52) - 52LL) << 52;
memcpy(&r, &a, sizeof(a));
return r;
}
static double sqlfcmp_fabs(double v)
{
if (v < 0)
return -v;
return v;
}
#endif
static double distance(double x, double y)
{
return (x - y) / epsilon(x);
}
/*
** Return the distance to the next representable number
*/
SQLITE_PRIVATE void _ulp(sqlite3_context *context, int argc, sqlite3_value
**argv)
{
sqlite3_result_double(context, epsilon(sqlite3_value_double(argv[0])));
}
/*
** Return the number of representable numbers between x and y based on the ULP
of x
** if only one parameter, it is y and x is 0.0
*/
SQLITE_PRIVATE void _ulps(sqlite3_context *context, int argc, sqlite3_value
**argv)
{
double x = 0.0;
double y = 0.0;
uintptr_t flag = (uintptr_t)sqlite3_user_data(context);
if ((argc == 0) || (argc > 2))
return;
if (argc == 1)
y = sqlite3_value_double(argv[0]);
else
{
x = sqlite3_value_double(argv[0]);
y = sqlite3_value_double(argv[1]);
}
sqlite3_result_double(context, distance(x, y));
}
/*
** Perform Floating Point (Double) Comparisons using ULP of first parameter
** first parameter will be 0.0 if only one parameter specified, which will be
y
** User Context defines operations
** flag == 0 x != y
** flag & 1 x < y
** flag & 2 x = y
** flag & 4 x > y
** Flag values are additive
** flag & 3 x <= y
** flag & 6 x >= y
** optional third parameter is ULPs of x to consider equal, defaults to 5
** if only one parameter then test if x within 5 ULPs of 0.0
*/
SQLITE_PRIVATE void _fpc(sqlite3_context *context, int argc, sqlite3_value
**argv)
{
double x = 0.0;
double y = 0.0;
double ulps = 0.0;
double delta = 5.0;
uintptr_t flag = (uintptr_t)sqlite3_user_data(context);
if ((argc == 0) || (argc > 3))
return;
if (argc == 1)
y = sqlite3_value_double(argv[0]);
else
{
x = sqlite3_value_double(argv[0]);
y = sqlite3_value_double(argv[1]);
}
if (argc > 2)
delta = sqlfcmp_fabs(sqlite3_value_double(argv[2]));
ulps = distance(x, y);
if ( ((flag & 2) && (sqlfcmp_fabs(ulps) <= delta)) /* flag & 2 means
test equal */
|| ((flag & 1) && (ulps < -delta)) /* flag & 1 means
test less than */
|| ((flag & 4) && (ulps > delta)) /* flag & 4 means
test greater than */
|| ((flag == 0) && (sqlfcmp_fabs(ulps) > delta))) /* no flags test
not equal */
sqlite3_result_int(context, 1);
else
sqlite3_result_int(context, 0);
}
/*
** If compiling as a builtin extension, don't export the initializer -- make it
SQLITE_PRIVATE
** Change name of initializer to sqlite3_init_<nameOfExtension>
*/
/* SQLite invokes this routine once when it loads the extension.
** Create new functions, collating sequences, and virtual table
** modules here. This is usually the only exported symbol in
** the shared library.
*/
#ifdef _WIN32
#ifndef SQLITE_CORE
__declspec(dllexport)
#endif
#endif
#ifdef SQLITE_CORE
static
#endif
int sqlite3_sqlfcmp_init(sqlite3 *db, char **pzErrMsg, const
sqlite3_api_routines *pApi)
{
int nErr = 0;
SQLITE_EXTENSION_INIT2(pApi);
nErr += sqlite3_create_function(db, "ulp", 1,
SQLITE_ANY|SQLITE_DETERMINISTIC, 0, _ulp, 0, 0);
nErr += sqlite3_create_function(db, "epsilon", 1,
SQLITE_ANY|SQLITE_DETERMINISTIC, 0, _ulp, 0, 0);
nErr += sqlite3_create_function(db, "ulps", -1,
SQLITE_ANY|SQLITE_DETERMINISTIC, 0, _ulps, 0, 0);
nErr += sqlite3_create_function(db, "fne", -1,
SQLITE_ANY|SQLITE_DETERMINISTIC, 0, _fpc, 0, 0);
nErr += sqlite3_create_function(db, "flt", -1,
SQLITE_ANY|SQLITE_DETERMINISTIC, (void*)1, _fpc, 0, 0);
nErr += sqlite3_create_function(db, "feq", -1,
SQLITE_ANY|SQLITE_DETERMINISTIC, (void*)2, _fpc, 0, 0);
nErr += sqlite3_create_function(db, "fle", -1,
SQLITE_ANY|SQLITE_DETERMINISTIC, (void*)3, _fpc, 0, 0);
nErr += sqlite3_create_function(db, "fgt", -1,
SQLITE_ANY|SQLITE_DETERMINISTIC, (void*)4, _fpc, 0, 0);
nErr += sqlite3_create_function(db, "fge", -1,
SQLITE_ANY|SQLITE_DETERMINISTIC, (void*)6, _fpc, 0, 0);
return nErr ? SQLITE_ERROR : SQLITE_OK;
}
#ifdef __cplusplus
}
#endif
> -----Original Message-----
> From: sqlite-users [mailto:[email protected]]
> On Behalf Of Warren Young
> Sent: Friday, 2 December, 2016 11:37
> To: SQLite mailing list
> Subject: Re: [sqlite] Datatype for prices (1,500)
>
> On Nov 30, 2016, at 6:53 PM, Keith Medcalf <[email protected]> wrote:
> >
> > sqlite> SELECT CASE WHEN (SELECT feq(0, sum(amount)) FROM transactions)
>
> What’s feq()? I don’t see it in the SQLite documentation.
>
> _______________________________________________
> sqlite-users mailing list
> [email protected]
> http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
_______________________________________________
sqlite-users mailing list
[email protected]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users