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);
Related
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?
I'm attempting to build a very simple function to convert units to/from a 'Base Unit of Measurement' (ie: various different units to/from KGs), but I'm running into some inconsistent results. When I convert a value to the Base UOM I get the expected value; however, when I convert it back, I'm getting a result that does not match my expectations.
I'm currently using the decimal (numeric) data type, on my Postgres database, but if I convert the quantity column to float, the outcome is inline with what I'm expecting.
Migrations
conversions_table
$table->decimal('lb', 18, 16, true);
// value = 0.4535923700000000
consumtion_table
$table->decimal('quantity', 16, 10);
Code
// Convert LBs to KG
$quantity * $conversions->lb
// Convert KGs to LBs
$quantity / $conversions->lb
Example
If a $value = 0.125 is converted to KGs it results in the function returning 0.0566990463 KGs; therefore, when I run the function in the reverse direction to return the value in KGs back to LBs, it results in 0.12500000011023113
But, if I change the data type to float(), that same function returns 0.05669904625 KGs, which then returns the expected result when converting back to LBs -> 0.125.
Summary
I'm sure this is a 'newbie' mistake and perhaps expected behaviour due to a misunderstanding of these different data types, but I'd be very thankful if someone could clarify what the best approach is tackling this kind of scenario!
You defined your column consumption.quantity as:
$table->decimal('quantity', 16, 10);
That's a numeric with a precision of 16 and a scale of 10. I.e. 10 fractional decimal digits.
The result of 0.125 * 0.45359237 is 0.05669904625, which requires 11 fractional decimal digits to be represented exactly. Since your column only allows 10, the value is rounded to 0.0566990463, thereby introducing the error. (Or there are additional links in the food chain that (also) round to a scale of 10.)
Current Lavarel documentation.
Quoting the current Postgres manual:
If the scale of a value to be stored is greater than the declared scale of the column, the system will round the value to the specified number of fractional digits.
Solution: Allow more fractional digits. Example with a scale of 16 in your table columns:
$table->decimal('quantity', 20, 16);
Or drop maximum precision and scale altogether and use plain numeric. Or whatever suits your needs.
To be precise, this is slightly incorrect:
If a $value = 0.125 is converted to KGs it results in the function returning 0.0566990463 KGs;
The calculation should return the exact number. It is later rounded to a scale of 10.
I have been working on a project with a lot of numbers inserted in a database table. Now that I finished the code, I was checking the values for errors and I noticed my value 3075277 would transform in 3075280 when inserted in the db and 3075255 would be 3075260.
The colummn type is Float. What should I change to disable the rounding? This one doesn't even have decimals numbers, why would it round like that? I use the default options, only changed collation to utf8_general_ci and change the type to varchar and lenght in some and float in others.
This issue is with MySQL, not Phpmyadmin.
FLOAT has 6-7 significant digits of precision, as you are seeing with the mangled values. By "significant digits", I mean starting anywhere:
1234567xxxx.
12345.67xxx
1.234567xxx
0.0000001234567xxx
That is the xxx is likely to be zeros or some kind of 'noise', not the original value you put into the column.
DOUBLE gives you about 16 significant digits.
DECIMAL(9,0) gives you 9 digits to the left of the decimal point, none afterwards. Sort of like INT.
DECIMAL(9,4) gives you 5 (9-4) digits to the left of the point; 4 afterwards.
What kinds of numbers are you storing? Money? Scientific measurements? Astronomical distances? DT's wealth?
Now you are using FLOAT type but getting error because you are saving big decimal number in the database. You should go for DOUBLE.
Although FLOAT and DOUBLE are similar because they store the value in approximate value, but that DOUBLE is 8-bytes, and FLOAT is 4-bytes.
A FLOAT is for single-precision, while a DOUBLE is for double-precision numbers.
MySQL uses four bytes for single-precision values and eight bytes for double-precision values.
There is a big difference from floating point numbers and decimal (numeric) numbers, which you can use with the DECIMAL data type. This is used to store exact numeric data values, unlike floating point numbers, where it is important to preserve exact precision, for example with monetary data.
So as in your case, for larger numbers you would want DOUBLE instead of FLOAT.
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
I am entering '35444650.00' as a float into my MySQL and it keeps reformatting to 35444648.00, any help welcome...
Floats only have a certain level of precision, you may be going beyond how precise a float data type can be. Try using a DOUBLE instead.
A float has 6 digits of precision. Use a double to get 15 or switch to a numeric(x,y). If you're interested, check out the storage requirements for MySQL for the different data types.
The MySQL manual claims that FLOAT, REAL, and DOUBLE PRECISION fields stores values as approximate and INTEGER, SMALLINT, DECIMAL, and NUMERIC fields stores values as exact.
I think best bet to overcome this precision issue is to use decimal.
Reference: http://dev.mysql.com/doc/refman/5.1/en/numeric-types.html
A higher precision alternative to float is DOUBLE. But looking at the example, I think the DECIMAL datatype might come in handy if the number of digits required after zero is small (around 2-4) and the number of digits before decimal is also small (around 10-12).
You are going past the level of precision possible. You need to define the float with more precision, i.e. "FLOAT(10,5)" would mean a float that can have 10 digits total with up to five after the decimal point.