So I know there have been multiple questions regarding Money and converting to and from cents.
Heck I have even asked another one, but I want to make a slightly different question so I hope there are no duplicates out there.
So I have created a function that takes a Dollar Value and sends it to CENTS.
But I think I have a slight problem with my code and hoping I can get it tweaked a little.
$money4 = "10.0001";
// Converted to cents, as you can see it's slightly off.
$money41 = "1001";
// So when "1001", get's put in the database, and then I return it back as a Money variable.
// We get, "$10.01"... but what I have now is a leak in my amounts... as it rounded up to the second point.
So to do what I have done, I have used to functions I made to do this.
// This essentially gets a DOLLAR figure, or the CENT's Figure if requested.
function stripMoney($value, $position = 0, $returnAs = "")
{
// Does it even have a decimal?
if(isset($value) && strstr($value, ".")) {
// Strip out everything but numbers, decimals and negative
$value = preg_replace("/([^0-9\.\-])/i","",$value);
$decimals = explode(".", $value);
// Return Dollars as default
return ($returnAs == "int" ? (int)$decimals[$position] : $decimals[$position]);
} elseif(isset($value)) {
// If no decimals, lets just return a solid number
$value = preg_replace("/([^0-9\.\-])/i","",$value);
return ($returnAs == "int" ? (int)$value : $value);
}
}
The next function I use is to generate the CENTS or return it back as dollars.
function convertCents($money, $cents = NULL, $toCents = TRUE)
{
if(isset($money)) {
if($toCents == TRUE) {
// Convert dollars to cents
$totalCents = $money * 100;
// If we have any cents, lets add them on as well
if(isset($cents)) {
$centsCount = strlen($cents);
// In case someone inputs, $1.1
// We add a zero to the end of the var to make it accurate
if($centsCount < 2) {
$cents = "{$cents}0";
}
// Add the cents together
$totalCents = $totalCents + $cents;
}
// Return total cents
return $totalCents;
} else {
// Convert cents to dollars
$totalDollars = $money / 100;
return $totalDollars;
}
}
}
And the final function that puts everything together. So we just use 1 function to merge the 2 functions together basically.
function convertMoney($value, $toCents = TRUE) {
if(isset($value) && strstr($value, ".")) {
return convertCents(stripMoney($value, 0), stripMoney($value, 1), $toCents);
} elseif(!empty($value)) {
return convertCents(stripMoney($value, 0), NULL, $toCents);
}
}
What I have done might be overkill, But I think it's fairly solid, other than this 1 detail, that I can see.
can anyone help me with these adjustments?
Do not use floating point arithmetic if you need exact answers. This applies to almost all languages, not just PHP. Read the big warning in the PHP manual.
Instead check out BC Math or the GMP extension. The latter only works with integer numbers so you are probably most interested in BC Math.
I think money_format is the function you were looking for...
<?php
$number = 1234.56;
// let's print the international format for the en_US locale
setlocale(LC_MONETARY, 'en_US');
echo money_format('%i', $number) . "\n";
// USD 1,234.56
// Italian national format with 2 decimals`
setlocale(LC_MONETARY, 'it_IT');
echo money_format('%.2n', $number) . "\n";
// Eu 1.234,56
?>
Related
I need to check in PHP if user entered a decimal number (US way, with decimal point: X.XXX)
Any reliable way to do this?
You can get most of what you want from is_float, but if you really need to know whether it has a decimal in it, your function above isn't terribly far (albeit the wrong language):
function is_decimal( $val )
{
return is_numeric( $val ) && floor( $val ) != $val;
}
if you want "10.00" to return true check Night Owl's answer
If you want to know if the decimals has a value you can use this answer.
Works with all kind of types (int, float, string)
if(fmod($val, 1) !== 0.00){
// your code if its decimals has a value
} else {
// your code if the decimals are .00, or is an integer
}
Examples:
(fmod(1.00, 1) !== 0.00) // returns false
(fmod(2, 1) !== 0.00) // returns false
(fmod(3.01, 1) !== 0.00) // returns true
(fmod(4.33333, 1) !== 0.00) // returns true
(fmod(5.00000, 1) !== 0.00) // returns false
(fmod('6.50', 1) !== 0.00) // returns true
Explanation:
fmod returns the floating point remainder (modulo) of the division of the arguments, (hence the (!== 0.00))
Modulus operator - why not use the modulus operator? E.g. ($val % 1 != 0)
From the PHP docs:
Operands of modulus are converted to integers (by stripping the decimal part) before processing.
Which will effectively destroys the op purpose, in other languages like javascript you can use the modulus operator
If all you need to know is whether a decimal point exists in a variable then this will get the job done...
function containsDecimal( $value ) {
if ( strpos( $value, "." ) !== false ) {
return true;
}
return false;
}
This isn't a very elegant solution but it works with strings and floats.
Make sure to use !== and not != in the strpos test or you will get incorrect results.
another way to solve this: preg_match('/^\d+\.\d+$/',$number); :)
The function you posted is just not PHP.
Have a look at is_float [docs].
Edit: I missed the "user entered value" part. In this case you can actually use a regular expression:
^\d+\.\d+$
I was passed a string, and wanted to know if it was a decimal or not. I ended up with this:
function isDecimal($value)
{
return ((float) $value !== floor($value));
}
I ran a bunch of test including decimals and non-decimals on both sides of zero, and it seemed to work.
is_numeric returns true for decimals and integers. So if your user lazily enters 1 instead of 1.00 it will still return true:
echo is_numeric(1); // true
echo is_numeric(1.00); // true
You may wish to convert the integer to a decimal with PHP, or let your database do it for you.
This is a more tolerate way to handle this with user input. This regex will match both "100" or "100.1" but doesn't allow for negative numbers.
/^(\d+)(\.\d+)?$/
// if numeric
if (is_numeric($field)) {
$whole = floor($field);
$fraction = $field - $whole;
// if decimal
if ($fraction > 0)
// do sth
else
// if integer
// do sth
}
else
// if non-numeric
// do sth
i use this:
function is_decimal ($price){
$value= trim($price); // trim space keys
$value= is_numeric($value); // validate numeric and numeric string, e.g., 12.00, 1e00, 123; but not -123
$value= preg_match('/^\d$/', $value); // only allow any digit e.g., 0,1,2,3,4,5,6,7,8,9. This will eliminate the numeric string, e.g., 1e00
$value= round($value, 2); // to a specified number of decimal places.e.g., 1.12345=> 1.12
return $value;
}
$lat = '-25.3654';
if(preg_match('/./',$lat)) {
echo "\nYes its a decimal value\n";
}
else{
echo 'No its not a decimal value';
}
A total cludge.. but hey it works !
$numpart = explode(".", $sumnum);
if ((exists($numpart[1]) && ($numpart[1] > 0 )){
// it's a decimal that is greater than zero
} else {
// its not a decimal, or the decimal is zero
}
the easy way to find either posted value is integer and float so this will help you
$postedValue = $this->input->post('value');
if(is_numeric( $postedValue ) && floor( $postedValue ))
{
echo 'success';
}
else
{
echo 'unsuccess';
}
if you give 10 or 10.5 or 10.0 the result will be success if you define any character or specail character without dot it will give unsuccess
How about (int)$value != $value?
If true it's decimal, if false it's not.
I can't comment, but I have this interesting behaviour.
(tested on v. 7.3.19 on a website for php testing online)
If you multiply 50 by 1.1 fmod gives different results than expected.
If you do by 1.2 or 1.3 it's fine, if you do another number (like 60 or 40) is also fine.
$price = 50;
$price = $price * 1.1;
if(strpos($price,".") !== false){
echo "decimal";
}else{
echo "not a decimal";
}
echo '<br />';
if(fmod($price, 1) !== 0.00){
//echo fmod($price, 1);
echo "decimal";
} else {
echo "not a decimal";
}//end if
Simplest solution is
if(is_float(2.3)){
echo 'true';
}
If you are working with form validation. Then in this case form send string.
I used following code to check either form input is a decimal number or not.
I hope this will work for you too.
function is_decimal($input = '') {
$alphabets = str_split($input);
$find = array('0','1','2','3','4','5','6','7','8','9','.'); // Please note: All intiger numbers are decimal. If you want to check numbers without point "." then you can remove '.' from array.
foreach ($alphabets as $key => $alphabet) {
if (!in_array($alphabet, $find)) {
return false;
}
}
// Check if user has enter "." point more then once.
if (substr_count($input, ".") > 1) {
return false;
}
return true;
}
function is_decimal_value( $a ) {
$d=0; $i=0;
$b= str_split(trim($a.""));
foreach ( $b as $c ) {
if ( $i==0 && strpos($c,"-") ) continue;
$i++;
if ( is_numeric($c) ) continue;
if ( stripos($c,".") === 0 ) {
$d++;
if ( $d > 1 ) return FALSE;
else continue;
} else
return FALSE;
}
return TRUE;
}
Known Issues with the above function:
1) Does not support "scientific notation" (1.23E-123), fiscal (leading $ or other) or "Trailing f" (C++ style floats) or "trailing currency" (USD, GBP etc)
2) False positive on string filenames that match a decimal: Please note that for example "10.0" as a filename cannot be distinguished from the decimal, so if you are attempting to detect a type from a string alone, and a filename matches a decimal name and has no path included, it will be impossible to discern.
Maybe try looking into this as well
!is_int()
I have two variables in a PHP program for billing statements, $charges and $payments.
$charges is the total amount due before any payments. $payments is the total amount received.
I calculate the balance due like so:
$balance_due = $charges-$payments;
Simple, except I am getting the following result:
$balance_due has -9.0949470177293E-13 for a value (expecting 0).
Both $charges and $payments have a value of 5511.53.
When I var_dump($charges) and var_dump($payments) they both show: float(5511.53)
This code (and === ):
if($charges == $payments){
error_log('they are the same');
}else{
error_log('they are not the same');
}
both result in false.
If I hard code: $charges = $payments = 5511.53; and run it then $balance_due = 0 as expected.
I am confused. What am I missing?
EDIT NOTES
I was able to use a user contributed function by Nitrogen found on the BC Math Functions page that was suggested I look at in order to come up with the following solution:
if(Comp($charges, $payments)===0){
$balance_due = 0;
}else{
$balance_due = ( $charges - $payments );
}
function Comp($Num1,$Num2,$Scale=null) {
// check if they're valid positive numbers, extract the whole numbers and decimals
if(!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num1,$Tmp1)||
!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num2,$Tmp2)) return('0');
// remove leading zeroes from whole numbers
$Num1=ltrim($Tmp1[1],'0');
$Num2=ltrim($Tmp2[1],'0');
// first, we can just check the lengths of the numbers, this can help save processing time
// if $Num1 is longer than $Num2, return 1.. vice versa with the next step.
if(strlen($Num1)>strlen($Num2)) return(1);
else {
if(strlen($Num1)<strlen($Num2)) return(-1);
// if the two numbers are of equal length, we check digit-by-digit
else {
// remove ending zeroes from decimals and remove point
$Dec1=isset($Tmp1[2])?rtrim(substr($Tmp1[2],1),'0'):'';
$Dec2=isset($Tmp2[2])?rtrim(substr($Tmp2[2],1),'0'):'';
// if the user defined $Scale, then make sure we use that only
if($Scale!=null) {
$Dec1=substr($Dec1,0,$Scale);
$Dec2=substr($Dec2,0,$Scale);
}
// calculate the longest length of decimals
$DLen=max(strlen($Dec1),strlen($Dec2));
// append the padded decimals onto the end of the whole numbers
$Num1.=str_pad($Dec1,$DLen,'0');
$Num2.=str_pad($Dec2,$DLen,'0');
// check digit-by-digit, if they have a difference, return 1 or -1 (greater/lower than)
for($i=0;$i<strlen($Num1);$i++) {
if((int)$Num1{$i}>(int)$Num2{$i}) return(1);
else
if((int)$Num1{$i}<(int)$Num2{$i}) return(-1);
}
// if the two numbers have no difference (they're the same).. return 0
return(0);
}
}
}
That solution worked for me. The answer provided by imtheman below also works and seems more efficient so I am going to use that one instead. Is there any reason not to use one or the other of these?
The way I solved this problem when I ran into it was using php's number_format(). From php documentation:
string number_format(float $number [, int $decimals = 0 ])
So what I would do is this:
$balance_due = number_format($charges-$payments, 2);
And that should solve your problem.
Note: number_format() will return a string, so to compare it you must use == (not ===) or cast it back into a (float) before comparison.
I need to know how to take
10.25 and turn it to 1025
basically it needs to remove full stop from any number for e.g.
1500.25 it should be 150025
$number = str_replace('.','',$number);
if the currency is a float: multiply with 100 (and cast the result to int).
$currency = 10.25;
$number = (int)($currency * 100); //1025
note that this solution will only get the first two decimals saved - if you have a number like 10.123, the 3 will simply be cut off without rounding.
Floating-point arithmetic is by it's definition not exact. Therefore it's worthwhile to NOT cast the value to a float if it's a string, and avoid casting it into a string if it's a float.
Here's a function which takes care to check the value type:
function toCents($value) {
// Strings with a dot is specially handled
// so they won't be converted to float
if (is_string($value) && strpos($value, '.') !== false) {
list($integer, $decimals) = explode('.', $value);
$decimals = (int) substr($decimals . '00', 0, 2);
return ((int) $integer) * 100 + $decimals;
// float values are rounded to avoid errors when a value
// like ".10" is saved as ".099"
} elseif (is_float($value) {
return round($value * 100);
// Other values are strings or integers, which are cast
// to int and multiplied directly.
} else {
return ((int) $value) * 100;
}
}
if you want replace one character only, use strtr instead str_replace
$number = str_replace('.','',$number);
and
$number = strtr($number, array('.', ''));
same output but strtr is better.
I need to check in PHP if user entered a decimal number (US way, with decimal point: X.XXX)
Any reliable way to do this?
You can get most of what you want from is_float, but if you really need to know whether it has a decimal in it, your function above isn't terribly far (albeit the wrong language):
function is_decimal( $val )
{
return is_numeric( $val ) && floor( $val ) != $val;
}
if you want "10.00" to return true check Night Owl's answer
If you want to know if the decimals has a value you can use this answer.
Works with all kind of types (int, float, string)
if(fmod($val, 1) !== 0.00){
// your code if its decimals has a value
} else {
// your code if the decimals are .00, or is an integer
}
Examples:
(fmod(1.00, 1) !== 0.00) // returns false
(fmod(2, 1) !== 0.00) // returns false
(fmod(3.01, 1) !== 0.00) // returns true
(fmod(4.33333, 1) !== 0.00) // returns true
(fmod(5.00000, 1) !== 0.00) // returns false
(fmod('6.50', 1) !== 0.00) // returns true
Explanation:
fmod returns the floating point remainder (modulo) of the division of the arguments, (hence the (!== 0.00))
Modulus operator - why not use the modulus operator? E.g. ($val % 1 != 0)
From the PHP docs:
Operands of modulus are converted to integers (by stripping the decimal part) before processing.
Which will effectively destroys the op purpose, in other languages like javascript you can use the modulus operator
If all you need to know is whether a decimal point exists in a variable then this will get the job done...
function containsDecimal( $value ) {
if ( strpos( $value, "." ) !== false ) {
return true;
}
return false;
}
This isn't a very elegant solution but it works with strings and floats.
Make sure to use !== and not != in the strpos test or you will get incorrect results.
another way to solve this: preg_match('/^\d+\.\d+$/',$number); :)
The function you posted is just not PHP.
Have a look at is_float [docs].
Edit: I missed the "user entered value" part. In this case you can actually use a regular expression:
^\d+\.\d+$
I was passed a string, and wanted to know if it was a decimal or not. I ended up with this:
function isDecimal($value)
{
return ((float) $value !== floor($value));
}
I ran a bunch of test including decimals and non-decimals on both sides of zero, and it seemed to work.
is_numeric returns true for decimals and integers. So if your user lazily enters 1 instead of 1.00 it will still return true:
echo is_numeric(1); // true
echo is_numeric(1.00); // true
You may wish to convert the integer to a decimal with PHP, or let your database do it for you.
This is a more tolerate way to handle this with user input. This regex will match both "100" or "100.1" but doesn't allow for negative numbers.
/^(\d+)(\.\d+)?$/
// if numeric
if (is_numeric($field)) {
$whole = floor($field);
$fraction = $field - $whole;
// if decimal
if ($fraction > 0)
// do sth
else
// if integer
// do sth
}
else
// if non-numeric
// do sth
i use this:
function is_decimal ($price){
$value= trim($price); // trim space keys
$value= is_numeric($value); // validate numeric and numeric string, e.g., 12.00, 1e00, 123; but not -123
$value= preg_match('/^\d$/', $value); // only allow any digit e.g., 0,1,2,3,4,5,6,7,8,9. This will eliminate the numeric string, e.g., 1e00
$value= round($value, 2); // to a specified number of decimal places.e.g., 1.12345=> 1.12
return $value;
}
$lat = '-25.3654';
if(preg_match('/./',$lat)) {
echo "\nYes its a decimal value\n";
}
else{
echo 'No its not a decimal value';
}
A total cludge.. but hey it works !
$numpart = explode(".", $sumnum);
if ((exists($numpart[1]) && ($numpart[1] > 0 )){
// it's a decimal that is greater than zero
} else {
// its not a decimal, or the decimal is zero
}
the easy way to find either posted value is integer and float so this will help you
$postedValue = $this->input->post('value');
if(is_numeric( $postedValue ) && floor( $postedValue ))
{
echo 'success';
}
else
{
echo 'unsuccess';
}
if you give 10 or 10.5 or 10.0 the result will be success if you define any character or specail character without dot it will give unsuccess
How about (int)$value != $value?
If true it's decimal, if false it's not.
I can't comment, but I have this interesting behaviour.
(tested on v. 7.3.19 on a website for php testing online)
If you multiply 50 by 1.1 fmod gives different results than expected.
If you do by 1.2 or 1.3 it's fine, if you do another number (like 60 or 40) is also fine.
$price = 50;
$price = $price * 1.1;
if(strpos($price,".") !== false){
echo "decimal";
}else{
echo "not a decimal";
}
echo '<br />';
if(fmod($price, 1) !== 0.00){
//echo fmod($price, 1);
echo "decimal";
} else {
echo "not a decimal";
}//end if
Simplest solution is
if(is_float(2.3)){
echo 'true';
}
If you are working with form validation. Then in this case form send string.
I used following code to check either form input is a decimal number or not.
I hope this will work for you too.
function is_decimal($input = '') {
$alphabets = str_split($input);
$find = array('0','1','2','3','4','5','6','7','8','9','.'); // Please note: All intiger numbers are decimal. If you want to check numbers without point "." then you can remove '.' from array.
foreach ($alphabets as $key => $alphabet) {
if (!in_array($alphabet, $find)) {
return false;
}
}
// Check if user has enter "." point more then once.
if (substr_count($input, ".") > 1) {
return false;
}
return true;
}
function is_decimal_value( $a ) {
$d=0; $i=0;
$b= str_split(trim($a.""));
foreach ( $b as $c ) {
if ( $i==0 && strpos($c,"-") ) continue;
$i++;
if ( is_numeric($c) ) continue;
if ( stripos($c,".") === 0 ) {
$d++;
if ( $d > 1 ) return FALSE;
else continue;
} else
return FALSE;
}
return TRUE;
}
Known Issues with the above function:
1) Does not support "scientific notation" (1.23E-123), fiscal (leading $ or other) or "Trailing f" (C++ style floats) or "trailing currency" (USD, GBP etc)
2) False positive on string filenames that match a decimal: Please note that for example "10.0" as a filename cannot be distinguished from the decimal, so if you are attempting to detect a type from a string alone, and a filename matches a decimal name and has no path included, it will be impossible to discern.
Maybe try looking into this as well
!is_int()
I want to get the length of integer values for validation in PHP.
Example:
Mobile numbers should be only 10 integer values. It should not be more than 10 or less than 10 and also it should not be included of alphabetic characters.
How can I validate this?
$num_length = strlen((string)$num);
if($num_length == 10) {
// Pass
} else {
// Fail
}
if (preg_match('/^\d{10}$/', $string)) {
// pass
} else {
// fail
}
This will work for almost all cases (except zero) and easily coded in other languages:
$length = ceil(log10(abs($number) + 1));
In my opinion, the best way is:
$length = ceil(log10($number))
A decimal logarithm rounded up is equal to length of a number.
If you are using a web form, make sure you limit the text input to only hold 10 characters as well to add some accessibility (users don't want to input it wrong, submit, get a dialog about their mistake, fix it, submit again, etc.)
Use intval function in loop,
See this example
<?php
$value = 16432;
$length=0;
while($value!=0) {
$value = intval($value/10);
$length++
}
echo "Length of Integer:- ".$length;
?>
$input = "03432 123-456"; // A mobile number (this would fail)
$number = preg_replace("/^\d/", "", $number);
$length = strlen((string) $number);
if ($number == $input && $length == 10) {
// Pass
} else {
// Fail
}
If you are evaluating mobile numbers (phone numbers) then I would recommend not using an int as your chosen data type. Use a string instead because I cannot forsee how or why you would want to do math with these numbers. As a best practice, use int, floats, etc, when you want/need to do math. Use strings when you don't.
From your question, "You want to get the lenght of an integer, the input will not accept alpha numeric data and the lenght of the integer cannot exceed 10. If this is what you mean; In my own opinion, this is the best way to achieve that:"
<?php
$int = 1234567890; //The integer variable
//Check if the variable $int is an integer:
if (!filter_var($int, FILTER_VALIDATE_INT)) {
echo "Only integer values are required!";
exit();
} else {
// Convert the integer to array
$int_array = array_map('intval', str_split($int));
//get the lenght of the array
$int_lenght = count($int_array);
}
//Check to make sure the lenght of the int does not exceed or less than10
if ($int_lenght != 10) {
echo "Only 10 digit numbers are allow!";
exit();
} else {
echo $int. " is an integer and its lenght is exactly " . $int_lenght;
//Then proceed with your code
}
//This will result to: 1234556789 is an integer and its lenght is exactly 10
?>
By using the assertion library of Webmozart Assert we can use their build-in methods to validate the input.
Use integerish() to validate that a value casts to an integer
Use length() to validate that a string has a certain number of characters
Example
Assert::integerish($input);
Assert::length((string) $input, 10); // expects string, so we type cast to string
As all assertions in the Assert class throw an Webmozart\Assert\InvalidArgumentException if they fail, we can catch it and communicate a clear message to the user.
Example
try {
Assert::integerish($input);
Assert::length((string) $input, 10);
} catch (InvalidArgumentException) {
throw new Exception('Please enter a valid phone number');
}
As an extra, it's even possible to check if the value is not a non-negative integer.
Example
try {
Assert::natural($input);
} catch (InvalidArgumentException) {
throw new Exception('Please enter a valid phone number');
}
I hope it helps 🙂
A bit optimazed answer in 2 or 3 steps depends if we allow negative value
if(is_int($number)
&& strlen((string)$number) == 10)
{
// 1 000 000 000 Executions take from 00:00:00.153200 to 00:00:00.173900
//Code
}
Note that will allow negative up to 9 numbers like -999999999
So if we need skip negatives we need 3rd comparision
if(is_int($number)
&& $number >= 0
&& strlen((string)$number) == 10)
{
// 1 000 000 000 Executions take from 00:00:00.153200
// to 00:00:00.173900 over 20 tests
}
Last case when we want from -1 000 000 000 to 1 000 000 000
if(is_int($number)
&& $number >= 0
&& strlen(str_replace('-', '', (string)$number)) == 10)
{
// 1 000 000 000 Executions take from 00:00:00.153200
// to 00:00:00.173900 over 20 tests
}
For compare
First naswer with regex
if (preg_match('/^\d{10}$/', $number)) {
// Fastest test with 00:00:00.246200
}
** Tested at PHP 8.0.12
** XAMPP 3.3.0
** Ryzen 7 2700
** MSI Radeon RX 5700 8G
Tested like
$function = function($number)
{
if(is_int($number)
&& $number >= 0
&& strlen((string)$number) == 10)
{
return true;
}
}
$number = 1000000000;
$startTime = DateTime::createFromFormat('U.u', microtime(true);
for($i = 0; $i < 1000000000; $i++)
{
call_user_func_array($function, $args);
}
$endTime = DateTime::createFromFormat('U.u', microtime(true);
echo $endTime->diff($startTime)->format('%H:%I:%S.%F');