5

I am having PHP calculation problems (I think - i am reasonably new to PHP).

I am using

function convertToBTCFromSatoshi($value){
    $BTC = ($value / 100000000 );
    return (float)$BTC;
}

If $value = 10000 the result is correct of "0.0001"

Yet if $value = 5000 the result is "-5.0E-5"

Any ideas please :-)

D.H.
  • 5,307
  • 3
  • 32
  • 45
Ford
  • 243
  • 1
  • 2
  • 6

7 Answers7

8

You really should not use floating point math for precision decimal values. It is a little-regarded truth of computer programming that when you're dealing with money you should avoid the usual IEEE floating-point math like the plague.

This is the reason why Bitcoin transactions store values as 64-bit integers. Because storing them as floating point numbers is error-prone.

For example, in your interactive PHP shell you can do something like this to find out where your math begins to break:

php > echo 10000.00000001 * 2;
20000.00000002
php > echo 100000.00000001 * 2;
200000.00000002
php > echo 1000000.00000001 * 2;
2000000

I'm running 64-bit PHP, too. If you are running 32-bit PHP the situation becomes pretty dire.

The take-away here is that overall precision of floating point numbers is limited. The bigger your number, the fewer meaningful decimal places it can have.

The correct way to address this problem is to use PHP's BC Math functions. You keep your data as strings, and BCMath will let you perform arbitrary-precision computations. It isn't as fast as floating point math, but it isn't exactly slow either.

/** Convert Satoshis to a string that can be displayed to users.
 *  input: $value Integer or string that can be parsed as an int.
 *  output: string (eg: "1.00400000")
 */
function convertToBTCFromSatoshi($value){
    return bcdiv( intval($value), 100000000, 8 );
}

If you want to trim the zeros from the end of the output, you can use the trim() function:

rtrim($value, "0"); // trim zeros from the right-hand side
slashingweapon
  • 376
  • 2
  • 5
  • Is that really a serious risk? Are you really afraid that someone will get 1 million BTC together - to steal 1 satoshi from you? – Nick ODell Jul 20 '13 at 00:55
  • It's also about small rounding errors, and decimal values that can't be precisely expressed in binary. And don't forget, Bitcoins are meant to be infinitely divisible. It is generally accepted that the protocol can be changed to support smaller units, if required. – slashingweapon Jul 20 '13 at 02:38
4

You should use bcmath, because PHP's native calculations won't fit your needs.

mmc4
  • 41
  • 1
1

The calculation is correct, but it becomes wrong when it shows the result to you.

Code:

function convertToBTCFromSatoshi($value) {
    $BTC = $value / 100000000 ;
    return $BTC;
}
function formatBTC($value) {
    $value = sprintf('%.8f', $value);
    $value = rtrim($value, '0') . ' BTC';
    return $value;
}
echo formatBTC(convertToBTCFromSatoshi(5000));

Outputs:

0.00005 BTC
Nick ODell
  • 29,396
  • 11
  • 72
  • 130
0

I am pretty sure you have found an answer by now, but for the sake of others. here is a pretty good way to solve that using bcmath's bcdiv

(PHP 4, PHP 5, PHP 7, PHP 8) bcdiv — Divide two arbitrary precision numbers Docs

$value = 5000;
$convertToBTCFromSatoshi = (function ($value): string {
  return bcdiv((string)$value, '100000000', 8);
})($value);
0

Satoshi to BTC

(Mathematical formula: Value * 10 ^ -8):

$satoshi = 5000; //Satoshi to btc
echo number_format(($satoshi)*(pow(10, -8)), 8, '.', ''); //Returns 0.00005000

BTC to Satoshi

(Mathematical formula: Value * 10 ^ 8):

$btc = 0.00005; //Btc to satoshi
echo ($btc)*(pow(10, 8)); //Returns 5000

Explanation

pow() - Exponential expression
number_format($var, 8, '.', '') - Returns a number eight decimal places
  • The standard mathematical operators in PHP are limited by the system's build - 32 or 64bit. This means your Satoshi to BTC conversion will be wrong on a 32bit system for values larger than 2^32. It's advised to use bcmath to convert between BTC & Satoshis. I typically use GMP on the satoshi amounts after, since it's faster. – karimkorun Apr 02 '16 at 12:39
-1

Easiest way

sprintf('%.8f', $amount); // 0.00000000
Nick ODell
  • 29,396
  • 11
  • 72
  • 130
webdev1
  • 21
  • 1
  • 1
    webdev1, please use code formatting when posting code. You can click edit on your own post to see how I did it. – Nick ODell Oct 30 '14 at 15:34
-1

Since you have not picked an answer, can I suggest a function? It is sufficient to handle your BTC transactions

function btc_output($x) {
    $f = sprintf('%0.08f', $x);
    $f = rtrim($f,'0');
    $f = rtrim($f,'.');
    return $f;
} 

$v = 5000/100000000;

echo btc_output($v); // 0.00005
Isaac
  • 1
  • 1