aboutsummaryrefslogtreecommitdiff
path: root/ext/misc/ieee754.c
diff options
context:
space:
mode:
authordan <dan@noemail.net>2020-07-13 18:04:27 +0000
committerdan <dan@noemail.net>2020-07-13 18:04:27 +0000
commita7f82d9f47ca75a2f47dc14f8a8deb6730d718d0 (patch)
treee2000d1d8b1b14297c1a8790c65dd90c4a20e2b9 /ext/misc/ieee754.c
parent7465787b97a0a09841e343630a07ba80f1399e4a (diff)
parent5b107654e965973e68c88f90a09a3cc53bac9d8b (diff)
downloadsqlite-a7f82d9f47ca75a2f47dc14f8a8deb6730d718d0.tar.gz
sqlite-a7f82d9f47ca75a2f47dc14f8a8deb6730d718d0.zip
Merge latest trunk changes with this branch.
FossilOrigin-Name: 5ee3c27e20d12a126fb773b428bb864102b949a5b26a8d5c523753dcedf4be10
Diffstat (limited to 'ext/misc/ieee754.c')
-rw-r--r--ext/misc/ieee754.c182
1 files changed, 161 insertions, 21 deletions
diff --git a/ext/misc/ieee754.c b/ext/misc/ieee754.c
index a67c91878..cb274c3f7 100644
--- a/ext/misc/ieee754.c
+++ b/ext/misc/ieee754.c
@@ -26,10 +26,64 @@
**
** Examples:
**
-** ieee754(2.0) -> 'ieee754(2,0)'
-** ieee754(45.25) -> 'ieee754(181,-2)'
-** ieee754(2, 0) -> 2.0
-** ieee754(181, -2) -> 45.25
+** ieee754(2.0) -> 'ieee754(2,0)'
+** ieee754(45.25) -> 'ieee754(181,-2)'
+** ieee754(2, 0) -> 2.0
+** ieee754(181, -2) -> 45.25
+**
+** Two additional functions break apart the one-argument ieee754()
+** result into separate integer values:
+**
+** ieee754_mantissa(45.25) -> 181
+** ieee754_exponent(45.25) -> -2
+**
+** These functions convert binary64 numbers into blobs and back again.
+**
+** ieee754_from_blob(x'3ff0000000000000') -> 1.0
+** ieee754_to_blob(1.0) -> x'3ff0000000000000'
+**
+** In all single-argument functions, if the argument is an 8-byte blob
+** then that blob is interpreted as a big-endian binary64 value.
+**
+**
+** EXACT DECIMAL REPRESENTATION OF BINARY64 VALUES
+** -----------------------------------------------
+**
+** This extension in combination with the separate 'decimal' extension
+** can be used to compute the exact decimal representation of binary64
+** values. To begin, first compute a table of exponent values:
+**
+** CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT);
+** WITH RECURSIVE c(x,v) AS (
+** VALUES(0,'1')
+** UNION ALL
+** SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971
+** ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
+** WITH RECURSIVE c(x,v) AS (
+** VALUES(-1,'0.5')
+** UNION ALL
+** SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075
+** ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
+**
+** Then, to compute the exact decimal representation of a floating
+** point value (the value 47.49 is used in the example) do:
+**
+** WITH c(n) AS (VALUES(47.49))
+** ---------------^^^^^---- Replace with whatever you want
+** SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v)
+** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n);
+**
+** Here is a query to show various boundry values for the binary64
+** number format:
+**
+** WITH c(name,bin) AS (VALUES
+** ('minimum positive value', x'0000000000000001'),
+** ('maximum subnormal value', x'000fffffffffffff'),
+** ('mininum positive nornal value', x'0010000000000000'),
+** ('maximum value', x'7fefffffffffffff'))
+** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
+** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
+**
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -51,8 +105,19 @@ static void ieee754func(
int isNeg;
char zResult[100];
assert( sizeof(m)==sizeof(r) );
- if( sqlite3_value_type(argv[0])!=SQLITE_FLOAT ) return;
- r = sqlite3_value_double(argv[0]);
+ if( sqlite3_value_type(argv[0])==SQLITE_BLOB
+ && sqlite3_value_bytes(argv[0])==sizeof(r)
+ ){
+ const unsigned char *x = sqlite3_value_blob(argv[0]);
+ int i;
+ sqlite3_uint64 v = 0;
+ for(i=0; i<sizeof(r); i++){
+ v = (v<<8) | x[i];
+ }
+ memcpy(&r, &v, sizeof(r));
+ }else{
+ r = sqlite3_value_double(argv[0]);
+ }
if( r<0.0 ){
isNeg = 1;
r = -r;
@@ -66,17 +131,31 @@ static void ieee754func(
}else{
e = a>>52;
m = a & ((((sqlite3_int64)1)<<52)-1);
- m |= ((sqlite3_int64)1)<<52;
+ if( e==0 ){
+ m <<= 1;
+ }else{
+ m |= ((sqlite3_int64)1)<<52;
+ }
while( e<1075 && m>0 && (m&1)==0 ){
m >>= 1;
e++;
}
if( isNeg ) m = -m;
}
- sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
- m, e-1075);
- sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
- }else if( argc==2 ){
+ switch( *(int*)sqlite3_user_data(context) ){
+ case 0:
+ sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
+ m, e-1075);
+ sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
+ break;
+ case 1:
+ sqlite3_result_int64(context, m);
+ break;
+ case 2:
+ sqlite3_result_int(context, e-1075);
+ break;
+ }
+ }else{
sqlite3_int64 m, e, a;
double r;
int isNeg = 0;
@@ -86,7 +165,7 @@ static void ieee754func(
isNeg = 1;
m = -m;
if( m<0 ) return;
- }else if( m==0 && e>1000 && e<1000 ){
+ }else if( m==0 && e>-1000 && e<1000 ){
sqlite3_result_double(context, 0.0);
return;
}
@@ -99,8 +178,13 @@ static void ieee754func(
e--;
}
e += 1075;
- if( e<0 ) e = m = 0;
- if( e>0x7ff ) e = 0x7ff;
+ if( e<=0 ){
+ /* Subnormal */
+ m >>= 1-e;
+ e = 0;
+ }else if( e>0x7ff ){
+ e = 0x7ff;
+ }
a = m & ((((sqlite3_int64)1)<<52)-1);
a |= e<<52;
if( isNeg ) a |= ((sqlite3_uint64)1)<<63;
@@ -109,6 +193,49 @@ static void ieee754func(
}
}
+/*
+** Functions to convert between blobs and floats.
+*/
+static void ieee754func_from_blob(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ if( sqlite3_value_type(argv[0])==SQLITE_BLOB
+ && sqlite3_value_bytes(argv[0])==sizeof(double)
+ ){
+ double r;
+ const unsigned char *x = sqlite3_value_blob(argv[0]);
+ int i;
+ sqlite3_uint64 v = 0;
+ for(i=0; i<sizeof(r); i++){
+ v = (v<<8) | x[i];
+ }
+ memcpy(&r, &v, sizeof(r));
+ sqlite3_result_double(context, r);
+ }
+}
+static void ieee754func_to_blob(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ if( sqlite3_value_type(argv[0])==SQLITE_FLOAT
+ || sqlite3_value_type(argv[0])==SQLITE_INTEGER
+ ){
+ double r = sqlite3_value_double(argv[0]);
+ sqlite3_uint64 v;
+ unsigned char a[sizeof(r)];
+ int i;
+ memcpy(&v, &r, sizeof(r));
+ for(i=1; i<=sizeof(r); i++){
+ a[sizeof(r)-i] = v&0xff;
+ v >>= 8;
+ }
+ sqlite3_result_blob(context, a, sizeof(r), SQLITE_TRANSIENT);
+ }
+}
+
#ifdef _WIN32
__declspec(dllexport)
@@ -118,16 +245,29 @@ int sqlite3_ieee_init(
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
+ static const struct {
+ char *zFName;
+ int nArg;
+ int iAux;
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ } aFunc[] = {
+ { "ieee754", 1, 0, ieee754func },
+ { "ieee754", 2, 0, ieee754func },
+ { "ieee754_mantissa", 1, 1, ieee754func },
+ { "ieee754_exponent", 1, 2, ieee754func },
+ { "ieee754_to_blob", 1, 0, ieee754func_to_blob },
+ { "ieee754_from_blob", 1, 0, ieee754func_from_blob },
+
+ };
+ int i;
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
- rc = sqlite3_create_function(db, "ieee754", 1,
- SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
- ieee754func, 0, 0);
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(db, "ieee754", 2,
- SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
- ieee754func, 0, 0);
+ for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_function(db, aFunc[i].zFName, aFunc[i].nArg,
+ SQLITE_UTF8|SQLITE_INNOCUOUS,
+ (void*)&aFunc[i].iAux,
+ aFunc[i].xFunc, 0, 0);
}
return rc;
}