numberformat error with bigs numbers in php - php

I'm developing a e-commerce and I have a problem when I want to format a number with number_format().
I have to set to my Stripe connection a number without decimals, so when I do all the calculations to have the final price of my shoppingcart I do:
$final_amount = number_format($final_amount, 2) * 100;
The result is a number that Stripe understands. I haven't got any problem with small numbers (like 970.25 or 1300.75 for example) but when I have a big amount like 15717.72 php throws the error "A non well formed numeric value encountered". I don't know if this is the problem, big numbers.
I've tried to parse previously $final_amount with floatval() and It didn't run either.
Someone knows the problem? thanks :)

A couple notes.
"A non well formed numeric value encountered" is a Notice, not an Error.
I don't believe 1300.75 works for you. The reason I don't believe this is you are only giving number_format two parameters. You are receiving that notice because number_format is formatting your number with a thousands separator ",".
$final_amount = number_format($final_amount, 2, ".", "") * 100;
should do the trick to remove that notice.

problem is not number_format() function but your calculation. You are multiplying a string with an integer. That does not work out so well.
$final_amount = number_format($final_amount * 100, 2);
Works just fine.
Update:
My conclusion was not completely correct. Multiplying an int with an string does work if the string is castable to int or float (see type juggling in PHP manual). But the string created by number_format() looks like this: "15,717.72". And thus cannot be cast to a number type.

Related

How to convert WEI to ETHER php

I need to convert string like 0x2fe84e3113d7b to the float type. This string comes from infura.io API as balance of the account. I've tried to use https://github.com/mbezhanov/ethereum-converter, but it makes no sense in this case (it always returns 0.00000 in any way). How to convert this string to 0.000842796652117371 with php?
use Bezhanov\Ethereum\Converter;
...
$this->converter = new Converter();
$weiValue = '0x1e1e83d93bb6ebb88bbaf';
dump($this->converter->fromWei($weiValue)); // returns 0.00000000000000000000000000000000000
$hexValue = hexdec($weiValue); // returns 2.2757423599815E+24
dump($this->converter->fromWei($hexValue)); // returns the same
I guess it caused by too long value on $hexValue (I mean converter can't convert long integers as it). But how to get the ether value from this hex?
https://www.investopedia.com/terms/w/wei.asp
1 Ether = 1,000,000,000,000,000,000 Wei (10^18)
and since this is currency, storing it as a floating point would be asinine, so it's got to be a 64-bit integer there.
Deleted my overwrought answer for a simple:
var_dump(
$wei = hexdec("0x2fe84e3113d7b"),
$wei / pow(10, 18)
);
Output:
int(842796652117371)
float(0.000842796652117370993)
Which, coincidentally, also illustrates why you don't want to use floats for currency. Also, WFM.
Still doesn't explain why you have:
$hexValue = hexdec($weiValue); // returns 2.2757423599815E+24
Quoted in your example as that's several orders of magnitude wrong for the supposed input.

Round to nearest whole number PHP

I figure this is easy enough but I am missing something here. Im using Stripe Connect and trying to calculate an application fee (in cents). The problem is my application fee sometimes has a decimal so it is throwing an error. I have tried using round() which gives me and ceil() but Im still getting a decimal and trailing zero in my answer so it returns an error.
$payment = bcmul($request->amount, 100); //112.00 - Convert to cents for stripe becomes 11200
$applicationFee = $payment * 0.021; //235.2 but should be just 235
print_r($applicationFee); //Should be whole number, round and ceil still provide me with decimal number IE 235.0 instead of 235
How can I make sure applicationFee is always a whole number with no decimal and rounded to nearest whole number?
You are correct as is described in the docs for ceil:
The return value of ceil() is still of type float
The method explanation also tells you that a float is returned:
float ceil ( float $value )
The return type for round() is also shown as a float in the docs for round as below:
float round ( float $val [, int $precision = 0 [, int $mode = PHP_ROUND_HALF_UP ]] )
You could use intval() instead as below:
intval($applicationFee);
Or try casting to an int as below:
(int) $applicationFee
If you want to keep your ceil or rounding you can call these first and then cast it as below:
intval(ceil($applicationFee));
(int) ceil($applicationFee);
This should work! (using round() function)
Documentation: Round()
$payment = bcmul($request->amount, 100);
$applicationFee = $payment * 0.021;
print_r(round($applicationFee));
round() should do the trick
$applicationFee = round($payment * 0.021);
You could add more parameters to round and so allow some decimals sill present in the result.
You say you tried it, but I wonder how.
Note
21 is is some countries the VAT percentage (taxes). Be careful how you round those numbers, as the tax office can be strickt
question: according to the docs round() returns a float, as it can also round to 1 or more decimals. But if the 2nd parameter is omitted you should get a whole number. How does a float equal to 235.0 influence the rest of the script?
Php automatically assigns a data type when you don't specifically tell it. For this to work you have to assign a data type that allows you to do the calculations you are trying to do.
Insure you dimension them with enough space for your calculations.
Then as a last step round it off to the precision that you desire and print it out.
It is highly unlikely that if you let PHP select the data type that you will get what you want. You have to carefully select for PHP what data type your variables are instantiated as.

Negative number with decimals to positive number in PHP

I'm creating an import based on xml. From the XML i get some values wich are negative amounts like -316.65.
Before I save this values to the database I have to convert them to positive amounts. I've tried the following:
$amount = $inkoopfactuur->td[7] * -1;
This worked but, the decimals where gone.
Is there a way to convert this witouth losing the decimals?
Thanks in advance!
There's a better way to do this:
$amount = abs($inkoopfactuur->td[7]);
However, it could be the case that the integer rounding is the result of something else -- there's not enough information in the question to be certain.
You probably have a different locale.
Check localeconv

getting an odd division error with PHP number_format()

my script calculates a number X by dividing two other numbers, A by B.
X=A/B
when i use number_format(A,2) on A before calculating X, i get a very odd number. Actual figures:
1,045.00 / 5 = 0.2
but if i don't use number_format on A before the division, i get the correct answer. Is number_format somehow making A into a non-number?
1045 / 5 = 209
number_format should be used only while pretty printing the number. Its return value should not used in calculation as you did.
Example:
If $A = 1045;
then number_format($A,2) will be 1,045.00 now if you treat 1,045.00 as a number it will be 1 as comma and remaining char will be ignored and 1/2 is 0.5 which you are getting.
You want round(A, 2), not number_format() which is for string representations (hence named "format").
The docs show that number_format returns a string. Have you tried casting the result of number_format() to a numeric type before your mathematical manipulation?
I had similar issues. It could be better if we use number format dec_point and thousand_separator parameters. you could use number_format($number, 2, '.', ''); It will help to remove your thousand separator
number_format makes it into a string with commas between thousands, and the comma will be confusing the divisor into thinking that's the decimels based on your locale.

PHP money string conversion to integer error

I have a small financial application with PHP as the front end and MySQL as the back end. I have ancient prejudices, and I store money values in MySQL as an integer of cents. My HTML forms allow input of dollar values, like "156.64" and I use PHP to convert that to cents and then I store the cents in the database.
I have a function that both cleans the dollar value from the form, and converts it to cents. I strip leading text, I strip trailing text, I multiply by 100 and convert to an integer. That final step is
$cents = (integer) ($dollars * 100);
This works fine for almost everything, except for a very few values like '156.64' which consistently converts to 15663 cents. Why does it do this?
If I do this:
$cents = (integer) ($dollars * 100 + 0.5);
then it consistently works. Why do I need to add that rounding value?
Also, my prejudices about storing money amounts as integers and not floating point values, is that no longer needed? Will modern float calculations produce nicely rounded and accurate money values adequate for keeping 100% accurate accounting?
If you want precision, you should store your money values using the DECIMAL data type in MySQL.
Your "prejudices" about floats will never be overcome - it's fundamental to the way they work. Without going into too much detail, they store a number based on powers of two and since not all decimal number can be presented this way, it doesn't always work. Your only reliable solution is to store the number as a sequence of digits and the location of the decimal point (as per DECIMAL type mentioned above).
I'm not 100% on the PHP, but is it possible the multiplication is converting the ints to floats and hence introducing exactly the problem you're trying to avoid?
Currency/money values should never be stored in a database (or used in a program) as floats.
Your integer method is fine, as is using a DECIMAL, NUMERIC or MONEY type where available.
Your problem is caused by $dollars being treated as a float and PHP doesn't have a better type to deal with money. Depending on when $dollars is being assigned, it could be being treated as a string or a float, but is certainly converted to a float if it's still a string for the * 100 operation if it looks like a float.
You might be better off parsing the string to an integer "money" value yourself (using a regex) instead of relying on the implicit conversions which PHP is doing.
The code you posted does the multiplication first, forcing a floating point calculation that introduces error, before converting the value to an integer. Instead, you should avoid floating point arithmetic entirely by reversing the order. Convert to integer values first, then perform the arithmetic.
Assuming previous code already validated and formatted the input, try this:
list($bills, $pennies) = explode('.', $dollars);
$cents = 100 * $bills + $pennies;
Your prejudice against floating point values to represent money is well founded because of truncation and because of values being converted from base-10 to base-2 and back again.
Casting does not round() as in round-to-nearest, it truncates at the decimal: (int)3.99 yields 3. (int)-3.99 yields -3.
Since float arithmetic often induces error (and possibly not in the direction you want), use round() if you want reliable rounding.
You should never ever store currency in floating point, because it always get results you don't expect.
Check out php BC Maths, it allow you to store your currency as string, then perform very high precision arithmetic on them.
Instead of using
$cents = (integer) ($dollars * 100);
you may want to try to use:
$cents = bcmul($dollars, 100, 2);
When converting from float to integer, the number will be rounded towards zero (src).
Read the Floating point precision warning.
There's no point in storing money as integer if you enter it through a floating point operation (no pun intended). If you want to convert from string to int and be consistent with your "prejudice" you can simply use string functions.
You can use an arbitrary precision library to divide by 10 (they handle numbers internally as strings), e.g. bcdiv() or gmp_div_q(), but of course, you could have also used it from the beginning for all the math.
Or you can use plain string functions:
<?php
// Quick ugly code not fully tested
$input = '156.64';
$output = NULL;
if( preg_match('/\d+(\.\d+)?/', $input) ){
$tmp = explode('.', $input);
switch( count($tmp) ){
case 1:
$output = $tmp[0];
break;
case 2:
$output = $tmp[0] . substr($tmp[1], 0, 2);
break;
default:
echo "Invalid decimal\n";
}
}else{
echo "Invalid number\n";
}
var_dump($output);
?>

Categories