diff options
author | dan <dan@noemail.net> | 2020-07-13 18:04:27 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2020-07-13 18:04:27 +0000 |
commit | a7f82d9f47ca75a2f47dc14f8a8deb6730d718d0 (patch) | |
tree | e2000d1d8b1b14297c1a8790c65dd90c4a20e2b9 /ext/misc/ieee754.c | |
parent | 7465787b97a0a09841e343630a07ba80f1399e4a (diff) | |
parent | 5b107654e965973e68c88f90a09a3cc53bac9d8b (diff) | |
download | sqlite-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.c | 182 |
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; } |