I'm new to math in PHP. I'm working on calculating cryptocurrencies that have up to 18 decimal places. What is the best way to calculate this precisely in PHP? Most answers on SO are for other coding languages and about rounding.
For example, when I do the below, I don't get 0.000000000000000001 as an answer.
$a= 0.000000000000000002;
$b= 0.000000000000000001;
$balance = $a - $b;
What I get is -8.0E-20 which is not 0.000000000000000001 (i think).
Also, [from here][1], it says float has a max of 14 decimal places but then it used the word "roughly."
[1]: https://www.php.net/manual/en/language.types.float.php#:~:text=The%20size%20of%20a%20float,the%2064%20bit%20IEEE%20format).
To keep your precision when displaying, you must use a Float Data type, see the floatval() function for more help.
Up to 18 places is an arbitrary amount more than the supported size, which is 14 on most machines depending on memory?
As mentioned in the comments using defined base units would solve the problem, to expand on that to be a more generalized solution that is more scalable, applicable to more algorithms, is to create a new class to hold this data type, dynamic float (d_float) is what I would use, cc_value = new d_float(value: int, pof10: int); cc_value * pof10; Where crypto currency value is described by a integer value that contains each digit and a second integer that can be applied to return the correct decimal value. Different base amounts could simply be extensions of the d_float class and therefore have fixed pof10 values?
Related
I am trying to create a column "amount" which should be able to take little decimal BTC values like 0.0000321, and as well normal int values like 10,15. What migration type can accept these?
In either MySQL or PostgresQL use DECIMAL for any monetary value. It's the only data type which stores as an exact value. FLOAT and DOUBLE are not stored as exact values and aren't good for doing precise math calculations. You can check the documentation on your database of choice for more.
You need to use decimal type. So you can set precision.
Ref:https://laravel.com/docs/8.x/migrations#column-method-decimal
The decimal method creates a DECIMAL equivalent column with the given
precision (total digits) and scale (decimal digits):
$table->decimal('column_name', $precision = 8, $scale = 2);
I have what I think it is an IEEE754 with single or double precision (not sure) and I'd like to convert it to decimal on PHP.
Given 4 hex value (which might be in little endian format, so basically reversed order) 4A,5B,1B,05 I need to convert it to a decimal value which I know will be very close to 4724.50073.
I've tried some online converters but they are far from the expected result so I'm clearly missing something.
If I echo 0x4A; I get 74 and the others are 91, 27 and 5. Not sure where to take it from here...
To convert it to float, use unpack. If the byte order is incorrect, you'll have to reverse it yourself before unpacking. 4 bytes (32 bits) usually means it's a float, 8 for double.
$bin = "\x4A\x5B\x1B\x05";
$a = unpack('f', strrev($bin));
echo $a[1]; // 3589825.25
I don't see any way how this maps to 4724.50073 directly tho. Without any more test data or manufacturer's manual this question is not fully answerable.
Speculation: judging from the size of the coordinate it's probably some sort of projection (XYZ or mercator) which can then be converted to WGS84 or whatever you need. Unfortunately there's no way to check since you haven't provided both latitude and longitude.
I have an customer invoice table in my MySQL database with a DECIMAL(10,2) field called price.
When fetching these values in php and calculating a sum amount,
ex: in the script
$totalAmount = 0; // initialised them to
while(records){
$amount = $inv_amount - ($pay_amount + $onamount); //float i guess. 2.22, 14.22
$totalAmount = $totalAmount + $amount; //float i guess. 2.22, 14.22 ..etc
}
when echo $totalAmount; it has a slight error in the final amount 0.01 however when dealing which large datasets around 20,000 this error becomes very considerable such as 200+
what is the safest way to do this when dealing with prices and such with these numbers in PHP? Or will I end up with potential rounding errors and things like that which are common when working with floating point data types?
is using
round
number_format
is the most suitable solution for this type of a financial application ?
Indeed, floating point numbers are not precise.
Either calculate in cent (multiply by 100 and calculate in integers), or calculate in strings using BC Math.
Try this:
$totalAmount = number_format($totalAmount, 2, '.', '');
If you need accuracy of 2 decimal points:
multiply value by 100
do your operations
divide by 100 and use number_format where appropriate
have you tried to
round(totalAmount, 2, PHP_ROUND_HALF_ODD);
it should see last decimal number - if it's odd - rounds down, if even - up
Well, to begin with, why sum in a php loop when you can do that in mysql? Moreover, just use integer multiplied by 100 and then divide by 100 once you need the final result.
You could use number_format
Ex:(for two decimal points)
$number = 12345.5667;
echo $result = number_format($number, 2);
Or you could use round function in MySql:
ROUND(number,2)
I recommend doing calculations on SQL Level with queries or views.
Firstly, using 64 bit integers on PHP is risky because; on overflow, it switches to float and you lose precision. When it's money, the problem is more serious. You should acually calculate a column with sql and just get the value from there. Money is a long type and it will often convert to float on php and your people will lose or win some cents depending on the mood of php engine. You can't work with decimal type on php, even if you multiply by 100 and keep it as an integer, long numbers with overflow will be converted to float automatically. Calculate your values on MYSQL if you want to use decimal numbers or convert your field into int.
If your server has an 32 bit cpu, the WORD length is 32 bit so integers in php engine are 32 bit integers. This makes the likelihood of overflow higher. On an 64 bit system, you can work more comfortably.
Using a round function on a money value is ridiculous and unprofessional in this case. Don't do it.
Reading this document will help you a lot.
php documentation of integers says
Integer overflow
If PHP encounters a number beyond the bounds of the integer type, it will be interpreted as a float instead. Also, an operation which results in a number beyond the bounds of the integer type will return a float instead.
Calculation on SQL level will ensure precision for money type. Getting the money type to php will result in a float value, and you can multiply it by 100 and divide by 100 later BUT it increases the rish of overflow again because php will use an 32 bit float number for storage. If you are using 32 bit float why is the data field decimal? So it's inconsistent if you do that. an 32 bit number i already not so big, and an 32 bit float loses some of its capacity on floating point, so it's more likely to reach over capacity when you multiply by 100.
USE SQL
In JavaScript this math operation returns:
1913397 / 13.054 = 146575.53240386088
but same operation in php returns:
1913397 / 13.054 = 146575.53240386
I guess this is due to rounding.
How can I extend php's precision?
From an article I wrote for Authorize.Net:
One plus one equals two, right? How about .2 plus 1.4 times 10? That equals 16, right? Not if you're doing the math with PHP (or most other programming languages):
echo floor((0.2 + 1.4) * 10); // Should be 16. But it's 15!
This is due to how floating point numbers are handled internally. They are represented with a fixed number of decimal places and can result in numbers that do not add up quite like you expect. Internally our .2 plus 1.4 times 10 example computes to roughly 15.9999999998 or so. This kind of math is fine when working with numbers that do not have to be precise like percentages. But when working with money precision matters as a penny or a dollar missing here or there adds up quickly and no one likes being on the short end of any missing money.
The BC Math Solution
Fortunately PHP offers the BC Math extension which is "for arbitrary precision mathematics PHP offers the Binary Calculator which supports numbers of any size and precision, represented as strings." In other words, you can do precise math with monetary values using this extension. The BC Math extension contains functions that allow you to perform the most common operations with precision including addition, subtraction, multiplication, and division.
A Better Example
Here's the same example as above but using the bcadd() function to do the math for us. It takes three parameters. The first two are the values we wish to add and the third is the number of decimal places we wish to be precise to. Since we're working with money we'll set the precision to be two decimal palces.
echo floor(bcadd('0.2', '1.4', 2) * 10); // It's 16 like we would expect it to be.
24151.40 - 31891.10 = -7739.699999999997
I grab these two numbers from a MySQL table with the type as decimal(14,2)
24151.40
31891.10
It is saved exactly as stated above and it echos exactly like that in PHP. But the minute I subtract the second value from the first value, I get a number -7739.699999999997 instead of -7,739.7. Why the extra precision? And where is it coming from?
From an article I wrote for Authorize.Net:
One plus one equals two, right? How about .2 plus 1.4 times 10? That equals 16, right? Not if you're doing the math with PHP (or most other programming languages):
echo floor((0.2 + 1.4) * 10); // Should be 16. But it's 15!
This is due to how floating point numbers are handled internally. They are represented with a fixed number of decimal places and can result in numbers that do not add up quite like you expect. Internally our .2 plus 1.4 times 10 example computes to roughly 15.9999999998 or so. This kind of math is fine when working with numbers that do not have to be precise like percentages. But when working with money precision matters as a penny or a dollar missing here or there adds up quickly and no one likes being on the short end of any missing money.
The BC Math Solution
Fortunately PHP offers the BC Math extension which is "for arbitrary precision mathematics PHP offers the Binary Calculator which supports numbers of any size and precision, represented as strings." In other words, you can do precise math with monetary values using this extension. The BC Math extension contains functions that allow you to perform the most common operations with precision including addition, subtraction, multiplication, and division.
A Better Example
Here's the same example as above but using the bcadd() function to do the math for us. It takes three parameters. The first two are the values we wish to add and the third is the number of decimal places we wish to be precise to. Since we're working with money we'll set the precision to be two decimal palces.
echo floor(bcadd('0.2', '1.4', 2) * 10); // It's 16 like we would expect it to be.
PHP doesn't have a decimal type like MySQL does, it uses floats; and floats are notorious for being inaccurate.
To cure this, look into number_format, e.g.:
echo number_format(24151.40 - 31891.10, 2, '.', '');
For more accurate number manipulation, you could also look at the math extensions of PHP:
http://www.php.net/manual/en/refs.math.php
This has to do with general float / double precision rates, which scientifically relates to 1.FRACTAL * 2^exponential power. Being that there's a prefix of 1, there's technically no such thing as zero, and the closest value you can obtain to 0 is 1.0 * 2 ^ -127 which is .000000[127 0s]00001
By rounding off your answer to a certain precision, the round factor will give you a more precise answer
http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_round