SQLite3 Decimal

SQLite3 Decimal
Login

SQLite3 Decimal

WARNING: this library is NOT ready for production yet. Use at your own risk!

SQLite3 Decimal

SQLite3 Decimal is an extension for SQLite implementing exact decimal arithmetic (within a bounded range). Numbers can be stored as strings or as 16-byte blobs conforming to IEEE 754 decimal128 format. Note that this representation is different from the one of SQLite's ieee754.c extension, which deals with a binary format encoding, not decimal. This extension is comparable to SQLite's decimal.c extension, but it provides more operations and uses a different internal representation for calculations.

The library provides 34 digits of decimal precision and an exponent range in [-6143,6144]. See also How much precision and range is needed for decimal arithmetic?

There is no manual yet. Please refer to the code documentation instead (read at the end of this document).

Quick Start

./configure
make
sqlite3 --cmd '.load ./libdecimal'

Sample Session

General Information

sqlite> select decVersion();
Decimal v0.1.2 (decNumber 3.68-p2)

sqlite> select * from decContext;
prec  emax  emin   round          
----  ----  -----  ---------------
34    6144  -6143  ROUND_HALF_EVEN

Creating decimals

Decimals can be created from strings, integers, or other decimals. The internal representation is based on IEEE 754 decimal128 format.

sqlite> select decBytes(dec('1.89'));
cf000000 00000000 00000000 00800722

sqlite> select decStr(dec('1.89'));
1.89

sqlite> select decBytes(dec(42));
42000000 00000000 00000000 00000822

sqlite> select decStr(dec(42));
42

Note: internally, SQLite3Decimal always manipulates numbers in IEEE's decimal128 format regardless of the input format. So, the output of a calculation is always a decimal128-formatted blob. To translate that into a string you have to use decStr().

Mathematical operations

sqlite> select decStr(decMul(decAdd('1.23', '45.0967', '-678.00000000000000001'), '-0.7891'));
498.453401030000000007891

sqlite> select decStr(decDivInt(9,4));
2

sqlite> select decStr(decSqrt(16.000));
4.00

sqlite> select decStr(decExp(-1)); -- e⁻¹
0.3678794411714423215955237701614609

sqlite> select decStr(decQuantize(decExp(-1), '.00')); -- Two fractional digits, please
0.37

Rounding

sqlite> select decStr(decDiv(1,7));
0.1428571428571428571428571428571429

sqlite> select * from decStatus; -- Was the result rounded?
Inexact result

Clear the status flags:

sqlite> delete from decStatus;

The rounding method can be adjusted at runtime by updating the context:

sqlite> select decStr(decDiv(1,3));
0.3333333333333333333333333333333333

sqlite> update decContext set round = 'ROUND_UP';
sqlite> select decStr(decDiv(1,3));
0.3333333333333333333333333333333334

Decimals stored as blobs

sqlite> create table T(n blob);
sqlite> insert into T values (dec('.843')), (dec('3427.19')), (dec('-28383.89'));
sqlite> select decStr(n) from T;
0.843    
3427.19  
-28383.89

sqlite> select decStr(n) from T where decLt(n, 10); -- Numbers less than 10
0.843
-28383.89

sqlite> select decStr(decMin(n)), decStr(decMax(n)), decStr(decSum(n)), decStr(decAvg(n)) from T;
-28383.89|3427.19|-24955.857|-8318.619

sqlite> select hex(n) from T;
4D010000000000000000000000400722
990B0700000000000000000000800722
CFF924000000000000000000008007A2

sqlite> select decBytes(n) from T;
4d010000 00000000 00000000 00400722
990b0700 00000000 00000000 00800722
cff92400 00000000 00000000 008007a2

Decimals stored as strings

Similarly to SQLite's own decimal.c extension, numbers stored as text can also be processed:

sqlite> create table U(n text);
sqlite> insert into U values ('0.123'), ('-34.20');
sqlite> select n from U;
 0.123
-34.20

select decStr(decMul(X.n, Y.n)) from U X, U Y where decLt(X.n, Y.n);
-4.20660

(Almost) equivalent formulation with SQLite's decimal.c extension:

sqlite> select decimal_mul(X.n, Y.n) from U X, U Y where decimal_cmp(X.n, Y.n) < 0;
-4.2066

When numbers are stored as text, you can even mix the two extensions if you are careful enough to convert SQLite3Decimal results back to strings:

sqlite> select decimal_add(decStr(decMul(X.n, Y.n)), '1.5') from U X, U Y where decimal_cmp(X.n, Y.n) < 0;
-2.70660

Trapping error conditions

The extension can be configured at runtime to raise or not to raise errors upon certain conditions. The conditions that should raise an error are found inside the decTraps virtual table:

sqlite> select flag from decTraps;
Conversion syntax  
Division by zero   
Division impossible
Division undefined 
Out of memory      
Invalid context    
Invalid operation    

sqlite> insert into decTraps values ('Inexact result');
sqlite> select decStr(decDiv(1,7));
Runtime error: Inexact

sqlite> select dec('1..2');
Runtime error: Conversion syntax
sqlite> delete from decTraps where flag = 'Conversion syntax';
sqlite> select decStr(dec('1..2'));
NaN

Build the code documentation

Install Doxygen and Graphviz, then:

make doc