Some may wonder why the integration is using bcmath, my explaination below:
Float, Decimal, Integer ? Mathematics in PHP & MySQLFloating point numbers are not accurate and should never be used for financial transactions.
A few examples of this:
$a = '35';
$b = '-34.99';
echo ($a + $b);
Results in
0.009999999999998 instead of
0.01echo floor((0.1+0.7)*10);
Results in
7 instead of the expected answer
8So we’re not able to use PHPs native operators for math calculations with decimals since those are treated as floats.
Then we have integer. A much more suited choice for financial transactions. One thing to keep in mind if choosing integer is the php limits. Those are for 32-bit build of PHP limited to integers in the range of
-2147483648 to
2147483647 while 64-bit
-9 223 372 036 854 775 808 to
9 223 372 036 854 775 807Imagine you have 100k XMR. Since monero has up to 12 decimals, to store 1 monero we need a number like this: 10^12 =
1 000 000 000 000That’s more than a 32-bit build can handle right there.
To store 100k XMR we would store:100000 * (10^12) =
100 000 000 000 000 000Let’s compare:
9 223 372 036 854 775 807 (64-bit limit)
100 000 000 000 000 000 (100k XMR)
Well, it’s still within the range, but if we increase it to 10 million XMR not even 64-bit can handle it.
Satoshi Nakamoto was pretty smart, he knew this. That’s probably why the limit is 21 million BTC. 1 Satoshi is the lowest unit of bitcoin, and 1 BTC = 100 000 000 Satoshi. Not only did he get it within the maximum integer value that can be stored. He also got it within the limit of floating point numbers, useful for languages like javascript, but that’s another story (you can
read more here)
21 000 000 * 100,000,000 = 2100000000000000
Comparing again:
9223372036854775807 (64-bit limit)
2147483647 (32-bit limit)
2100000000000000 (Max BTC)
As we can see, bitcoin fit’s within these ranges.
We want our application to be scalable, there is always a good chance we’re going to want to add another currency with it’s own unique rules at one point.
So monero cannot ultimately be stored as integer if we intend to preserve all the decimals. We could reduce the decimals to 8 like bitcoin. That will take care of it. But there is one unexplored alternative I’d like to present first: decimals and arbitrary precision math.
If you enable an extension for PHP called “bcmath” you will be able to go beyond the limits of your system. With arbitrary precision mathematics you’ll have a binary calculator which supports numbers of any size and precision represented as strings.
By using BCMath, we can do calculations and know with certainty that the numbers are 100% correct at all times, with every single satoshi accounted for.
Not only that, but we get to store numbers like they are supposed to be stored in database. Just like they look. By using MySQL DECIMAL, we can store the smallest unit of monero as:
0.000000000001.
Let us repeat the examples we did for float with bcmath:
$a = '35';
$b = '-34.99';
echo bcadd($a,$b);
Results in
0.01echo bc::floor(bcmul(bcadd('0.1','0.7'),'10'));
Results in
8Large numbers is no problem at all, let us multiply the 64-bit max limit by itself:
echo bcmul('9223372036854775807','9223372036854775807');
Results in
85070591730234615847396907784232501249Check for yourself (if you can even find a calculator doing that!
hint: Wolfram Alpha)
Similarly small amounts:
echo bcmul('0.000000000001','0.000000000001');
0.000000000000000000000001But before you do that, make sure to set
bcscale() to something high enough.
The script comes with a wrapper class for bc math, this will make it a tad easier to work with.
As for downsides to using bcmath:
- You need to have the bcmath extension (very easy to install, WHM/cPanel servers has it as a tick box)
- Not as fancy using functions to do operations as using + - * /, e.g bcadd(1,2) instead of 1+2
- Some functions available for php that is not integrated in bcmath, example is the php function “floor”, however there are re-implementations for this (I have included some in the wrapper)
- A bit slower… (But no big deal!)
So all calculations in the script are done using bcmath, the numbers are treated like strings and stored as decimals in the database. Only when we transfer money out do we convert to integer, and we do that outside of php as well (in json, with parameter JSON_NUMERIC_CHECK)