I work on a productivity suite that requires precision calculations. 99% of the time we do these server side using the bcmath library, because for the handling of money in the millions of dollars using floating point math sucks big time.
Clients want some real time functionality so this afternoon I broke down and wrote a brief library with the Prototype framework to expose the PHP bcmath lib to synchronous calls from the client.
In addition to all the standard bcmath functions I include two extra functions - BC.product and BC.sum. These functions take an array of numbers and aggregate them (to avoid the overhead of repeated SJAX calls).
Note that the JS here is written to put the browser in synchronous mode unlike the usual asynchronous requirements. Since the PHP side doesn’t try to start a database or any other overhead actions it should be lightning fast. It has to be since a synchronous request locks the browser up until it finishes - so if you use this code avoid spamming the calls (that’s why BC.product and BC.sum are added).
It would be preferable to have a js bcmath equivalent in the client side, but that’s beyond my skill.
I’ve been testing the code in the Firebug console, and it seems to be working.
This is very small so I’ll share it with everyone here. I could use help in verifying it works across all browsers. MIT license…
js side…
BC: {
scale: 2,
_request: function ( f, p ) {
return new Ajax.Request( 'bcmath.php', {
method: 'post',
parameters: {
'call': f,
'params[]': p,
'scale' : this.scale
},
asynchronous: false
}).transport.responseText;
},
add: function ( l, r ) { return this._request( 'add', [ l, r ]); },
comp: function ( l, r ) { return this._request( 'comp', [ l, r ]); },
div: function ( l, r ) { return this._request( 'div', [ l, r ]); },
mod: function ( l, m ) { return this._request( 'mod', [ l, m ]); },
mul: function ( l, r ) { return this._request( 'mul', [ l, r ]); },
pow: function ( l, r ) { return this._request( 'pow', [ l, r ]); },
powmod: function ( l, r, m ) { return this._request( 'powmod', [ l, r, m ]); },
product: function ( a ) { return this._request( 'product', a ); },
sqrt: function ( o ) { return this._request('sqrt', p); },
sub: function ( l, r ) { return this._request( 'sub', [ l, r ] ); },
sum: function ( a ) { return this._request('sum', a ); }
}
The “bcmath.php” file that js above must call.
<?php
/**
* This code is called SYNCRHONOUSLY! This means this must act as FAST AS POSSIBLE.
*/
header("HTTP/1.0 200");
header("Content-Type: text/plain");
header("Pragma: No-cache");
switch ($_POST['call']) {
case 'add': echo bcadd($_POST['params'][0], $_POST['params'][1], $_POST['scale']); break;
case 'comp': echo bccomp($_POST['params'][0], $_POST['params'][1], $_POST['scale']); break;
case 'div': echo bcdiv($_POST['params'][0], $_POST['params'][1], $_POST['scale']); break;
case 'mod': echo bcmod($_POST['params'][0], $_POST['params'][1]); break;
case 'mul': echo bcmul($_POST['params'][0], $_POST['params'][1], $_POST['scale']); break;
case 'pow': echo bcpow($_POST['params'][0], $_POST['params'][1], $_POST['scale']); break;
case 'powmod': echo bcmul($_POST['params'][0], $_POST['params'][1], $_POST['params'][2], $_POST['scale']); break;
case 'sqrt': echo bcsqrt($_POST['params'][0], $_POST['scale']); break;
case 'sub': echo bcsub($_POST['params'][0], $_POST['params'][1], $_POST['scale']); break;
case 'product':
$total = '0';
foreach ($_POST['params'] as $p) {
$total = bcmul( $total, $p, $_POST['scale']);
}
echo $total;
break;
case 'sum':
$total = '0';
foreach ($_POST['params'] as $p) {
$total = bcadd( $total, $p, $_POST['scale']);
}
echo $total;
break;
default:
header("HTTP/1.0 400");
echo 'Bad Request.';
break;
}
exit;
I probably should use $_GET instead of $_POST in the final version.