I have a number that needs to be rounded up to a specific decimal, is there any function in PHP to do that?
I need every number (which reflects an amount of money) to have a specific decimal number.
For example:
The decimal needs to be 25, so if I got $ 25.50 I need it to be $ 26.25, and if I got $ 25.10 it needs to be $ 25.25.
I've checked PHP round(), and specifically ceil(), and I've come across this answer in Python, but I'm not sure it applies to my case, because what I need is different.
Any ideas? Even pseudo code as a tip on where to start will help me. Thanks!
I think you need a custom function, something like this:
function my_round($number, $decimal = 0.25) {
$result = floor($number) + $decimal;
if ($result < $number) $result = ceil($number) + $decimal;
return $result;
}
print my_round(25.50);
I modified this answer for your case:
<?php
function roundUp($number){
$int = floor($number);
$float = $number-$int;
if ($float*10 < 2.5)
$result = $int;
else
$result = ceil($number);
$result+= 0.25;
echo $number." becomes ".$result."\n";
}
roundUp(25.50);
roundUp(25.10);
Look for demo here
Following axiac's advice mentioned in the comments and following this thread, the best way to deal with floating point numbers in the context of currencies, is to treat the dollars and cents' values as 2 separate entities.
One way I can think of it to split the numbers before and after the decimal into 2 separate variables and process accordingly.
<?php
function customRound($amount){
$amount = strval($amount);
if(preg_match('/(\d+)\.?(\d{1,2})?/', $amount, $matches) !== 1){
throw new \Exception("Invalid amount.");
}
$dollars = intval($matches[1]);
$cents = intval($matches[2] ?? 0);
if($cents < 10) $cents *= 10;
if($cents <= 25) return $dollars . ".25";
return ($dollars + 1) . ".25";
}
$tests = [25.51,25.49,26.25,25.10,25.49];
foreach ($tests as $test){
echo $test," => ",customRound($test),PHP_EOL;
}
Here's another approach:
<?php
function roundUp($number, $decimal=0.25){
$dollars = floor($number);
$cents = $number - $dollars;
if($cents > $decimal) {
++$dollars;
}
return $dollars + $decimal;
}
echo roundUp(25.50).PHP_EOL;
echo roundUp(25.10);
UPDATE - the problem was actually completely out of the rounding function. It seems as though $price (used in woo commerce) is a string and for some reason I can't use it in a calculation. If I simply return $price, no problem. All of this was fine when I was simply returning the value of the other function.
function xa_only_sale_price($price, $product)
{
error_log ("price in the beginning is " . $price);
if(!is_cart() && !is_checkout() && !is_ajax()){
if ($product->is_type('simple') || $product->is_type('variation')) {
$price = regularPriceHTML_for_simple_and_variation_product($price, $product);
$val = (float)$price;
error_log( "ceiling = " . ceil($val* 2) / 2); // 0 printed to log
return ceil($val* 2) / 2; this returns 0
// return roundNum(regularPriceHTML_for_simple_and_variation_product($price, $product));
}
}
error_log ("price before call is " . $price); // this returns 0
// return roundNum($price); //this is never in use
}
--------------- original post-----------------------------
I am new to php - thank you for all of the help in advance. I am assuming that this issue has something to do with data types but I haven't been able to figure this one out.
In this example $num is the price of a woocommerce product. If I simply return $num I see the price that I am expecting to see. I am simply trying to round the value in this function (I simplified the function for the sake of the question).
function roundNum($num){
$nearest = 0.50;
return ($num / $nearest) ;
This returns 0 to the browser. However, forcing the value of $num results in a valid calculation and return.
function roundNum($num){
$num = 100.0;
$nearest = 0.50;
return ($num / $nearest) ;
The simplest solution is to typecast your input. A small example in your case is:
function roundNum($num){
$nearest = 0.50;
$result = (float) $num/ (float) $nearest;
return $result;
}
Read more about typecasting here
EDIT:
As it turns out your $num is a string. You can change this to a type float and make your calculations, like so:
$num = "48.2";
$float = (float)$num;
echo ceil($float * 2) / 2;
In this example your number is always rounded up by 0.5, so in this case to 48.5
Demo
I'm having problems trying to compare two Arabic-language strings in a PHP script to see if they match. I've tried setting the internal encoding to UTF-8 with mb_internal_encoding, I've tried a simple if ($x == $y) expression, I've tried strcmp()... no dice. Any idea what I'm doing wrong? Does PHP have problems with doing string comparisons with non-English text?
Thanks!
Here is a code excerpt:
// Chop up HTML content into bits
$threadPieces = explode('</div>', $innerHTML);
// Chop up the HTML bits into data entries
$strippedThreadPieces = strip_tags($threadPieces[1]);
$threadInfo = explode('-', $strippedThreadPieces);
$threadTitleExists = trim($threadTitleExists, 'thread_title_');
$postername = "مراسل";
if (($threadTitleExists > 100000) && ($threadInfo[0] === $postername))
{
echo 'Thread title:';
echo strip_tags($threadPieces[0]);
echo '<p>';
}
else
{
}
I think the problem is the character set of your php file, try to save it using utf-8 character set.
The way I did this was using the function mb_ereg which is regular expression match with multibyte support.
Here's one I'm using
//from http://www.phperz.com/article/14/1029/31806.html
function mb_split_str($str) {
preg_match_all("/./u", $str, $arr);
return $arr[0];
}
//based on http://www.phperz.com/article/14/1029/31806.html, added percent
function mb_similar_text($str1, $str2, &$percent) {
$arr_1 = array_unique(mb_split_str($str1));
$arr_2 = array_unique(mb_split_str($str2));
$similarity = count($arr_2) - count(array_diff($arr_2, $arr_1));
$percent = ($similarity * 200) / (strlen($str1) + strlen($str2) );
return $percent;
}
So
$var = mb_similar_text('عمار', 'ياسر', $per);
output: $var = 2, $per = 25
I could never think it would be even challenging but is there any function to calculate a percentage of a number or do I have to write a custom function?
Example:
$result=percent(25,100); //doing the 100%25 operation
$result = 4
function percentage($part, $whole = 100) {
settype($part, "float");
settype($whole, "float");
$formule = ($part / $whole) * 100;
return $formule . " %";
}
echo percentage(25); // returns 25 %
echo percentage(91.8, 176); // returns 52,15909090909091 %
Voila ;-) Could be improved more, but i saw that you figured it out for yourself, so not needed anymore..
I want the user to be able to type in a fraction like:
1/2
2 1/4
3
And convert it into its corresponding decimal, to be saved in MySQL, that way I can order by it and do other comparisons to it.
But I need to be able to convert the decimal back to a fraction when showing to the user
so basically I need a function that will convert fraction string to decimal:
fraction_to_decimal("2 1/4");// return 2.25
and a function that can convert a decimal to a faction string:
decimal_to_fraction(.5); // return "1/2"
How can I do this?
Sometimes you need to find a way to do it and rounding is acceptable. So if you decide what range of rounding works out for you you can build a function like this. To convert a decimal into the fraction that it most closely matches. You can extend the accuracy by adding more denominators to be tested.
function decToFraction($float) {
// 1/2, 1/4, 1/8, 1/16, 1/3 ,2/3, 3/4, 3/8, 5/8, 7/8, 3/16, 5/16, 7/16,
// 9/16, 11/16, 13/16, 15/16
$whole = floor ( $float );
$decimal = $float - $whole;
$leastCommonDenom = 48; // 16 * 3;
$denominators = array (2, 3, 4, 8, 16, 24, 48 );
$roundedDecimal = round ( $decimal * $leastCommonDenom ) / $leastCommonDenom;
if ($roundedDecimal == 0)
return $whole;
if ($roundedDecimal == 1)
return $whole + 1;
foreach ( $denominators as $d ) {
if ($roundedDecimal * $d == floor ( $roundedDecimal * $d )) {
$denom = $d;
break;
}
}
return ($whole == 0 ? '' : $whole) . " " . ($roundedDecimal * $denom) . "/" . $denom;
}
I think I'd store the string representation too, as, once you run the math, you're not getting it back!
And, here's a quick-n-dirty compute function, no guarantees:
$input = '1 1/2';
$fraction = array('whole' => 0);
preg_match('/^((?P<whole>\d+)(?=\s))?(\s*)?(?P<numerator>\d+)\/(?P<denominator>\d+)$/', $input, $fraction);
$result = $fraction['whole'] + $fraction['numerator']/$fraction['denominator'];
print_r($result);die;
Oh, for completeness, add a check to make sure $fraction['denominator'] != 0.
To can use PEAR's Math_Fraction class for some of your needs
<?php
include "Math/Fraction.php";
$fr = new Math_Fraction(1,2);
// print as a string
// output: 1/2
echo $fr->toString();
// print as float
// output: 0.5
echo $fr->toFloat();
?>
Here is a solution that first determines a valid fraction (although not necessarily the simplest fraction). So 0.05 -> 5/100. It then determines the greatest common divisor of the numerator and denominator to reduce it down to the simplest fraction, 1/20.
function decimal_to_fraction($fraction) {
$base = floor($fraction);
$fraction -= $base;
if( $fraction == 0 ) return $base;
list($ignore, $numerator) = preg_split('/\./', $fraction, 2);
$denominator = pow(10, strlen($numerator));
$gcd = gcd($numerator, $denominator);
$fraction = ($numerator / $gcd) . '/' . ($denominator / $gcd);
if( $base > 0 ) {
return $base . ' ' . $fraction;
} else {
return $fraction;
}
}
# Borrowed from: http://www.php.net/manual/en/function.gmp-gcd.php#69189
function gcd($a,$b) {
return ($a % $b) ? gcd($b,$a % $b) : $b;
}
This includes a pure PHP implementation of the gcd although if you are sure the gmp module is installed you could use the one that comes with gcd.
As many others have noted you need to use rational numbers. So if you convert 1/7 to a decimal then try to convert it back to a decimal you will be out of luck because the precision lost will prevent it from getting back to 1/7. For my purposes this is acceptable since all the numbers I am dealing with (standard measurements) are rational numbers anyway.
Buddies, can this help?
[]s
function toFraction($number) {
if (!is_int($number)) {
$number = floatval($number);
$denominator = round(1 / $number);
return "1/{$denominator}";
}
else {
return $number;
}
}
Little improvement on above, but keepin it simple.
function dec2frac($f) {
$base = floor($f);
if ($base) {
$out = $base . ' ';
$f = $f - $base;
}
if ($f != 0) {
$d = 1;
while (fmod($f, 1) != 0.0) {
$f *= 2;
$d *= 2;
}
$n = sprintf('%.0f', $f);
$d = sprintf('%.0f', $d);
$out .= $n . '/' . $d;
}
return $out;
}
An approach would be to retrieve the decimal value and multiply it by 2, 3, 4 and so on until you get an integer number.
However, I'd stick with the answer given by Derek. Guess what happens when a user inserts n/(n+1) with n high. Such an algorithm would have to scan all the numbers up to n+1.
Not to mention it is likely you'll end up with approximation problems.
You'll have to face a serious problem, because floats are not precise enough.
When you'll have to deal with 1.3333, PHP will make an estimate of this value... So you will never be able to convert it to 1 1/3.
It seems to be simple to overcome, but if you want your program to differentiate 1/7901 (~ 1,2656625743576762435134793064169e-4) with 1/7907 (~
1,2647021626406981155937776653598e-4) precisely... this will be a real hell !!
IMHO, if you want to deal with maths, you should rely on an external library... or try to make PHP communicate with Matlab.
If you want to know more, i suggest you dig in floating point problems... Starting with wikipedia.
A variation of Jir's approach could actually work if only a limited amount of denominators are used : multiply everything by the least common denominators (and round the result to discard any remaining decimals due to approximation).
I.e.: if you only have to deal with halfs, thrids and quarters, just multiply everything by 12.
And also if you know the common denominator, this should greatly reduce the search speed by knowing exactly which numbers to search instead of searching all n+1 possible.
If you have to deal with lots of unusual fractions, like 1/7, 1/13, etc. well, stick to Derek's solution and store the original value too.
The fraction to decimal is quite straightforward and there are lots of solutions. I'd go with trimming the string, replacing spaces with '+', and anything other than space,/,. or digits with '' then running it through 'eval'.
The decimal to fraction is virtually impossible to do correctly - not least because your decimal fraction would probably have to be converted to binary first - at which point you loose a lot of precision. As an academic exercise.....If you can live with the difference between 20976/41953 and 1/2 then you could try a fuzzy match for a predefined number of fractions:
(there's probably a neater way of implementing the same algorithm - but I'll leave that as an exercise for the reader).
define('DECIMAL_DIGITS',5);
function decimal_2_frac($inp_decimal)
{
static $fracs;
if (!is_array($fracs)) {
init_fracs($fracs);
}
$int_part=(integer)$inp_decimal;
$inp_decimal=$inp_decimal-$int_part;
$candidate='';
$distance=10;
foreach ($fracs as $decimal=>$frac) {
if (abs($decimal-$inp_decimal)<$distance) {
$candidate=$frac;
$distance=abs($decimal-$inp_decimal);
}
if (abs($decimal-$inp_decimal)>$distance) {
break;
}
}
return $int_part . ' ' . $candidate;
}
function init_fracs(&$fracs)
{
$fracs=array();
for ($x=2;$x<(5*DECIMAL_DIGITS);$x++) {
// there's probably a beter way to calculate the loop limit
for ($y=1; $y<$x; $y++) {
$decimal=round($y/$x,DECIMAL_DIGITS);
$frac="$x/$y";
if (!array_key_exists($decimal,$fracs)) {
$fracs[$decimal]=$frac;
}
}
}
}
But personally, I'd just store the original representation in a seperate field in the database.
function dec2frac($f)
{
$d = 1
while (fmod($f, 1) != 0.0) {
$f *= 2;
$d *= 2;
}
$n = sprintf('%.0f', $f);
$d = sprintf('%.0f', $d);
return array($n, $d);
}
Then $f == $n / $d
For example:
print_r(dec2frac(3.1415926));
Outputs:
Array
(
[0] => 3537118815677477 // $n
[1] => 1125899906842624 // $d
)
I made a blog post with a couple solutions for this, the most recent approach I took is:
http://www.carlosabundis.com/2014/03/25/converting-decimals-to-fractions-with-php-v2/
function dec2fracso($dec){
//Negative number flag.
$num=$dec;
if($num<0){
$neg=true;
}else{
$neg=false;
}
//Extracts 2 strings from input number
$decarr=explode('.',(string)$dec);
//Checks for divided by zero input.
if($decarr[1]==0){
$decarr[1]=1;
$fraccion[0]=$decarr[0];
$fraccion[1]=$decarr[1];
return $fraccion;
}
//Calculates the divisor before simplification.
$long=strlen($decarr[1]);
$div="1";
for($x=0;$x<$long;$x++){
$div.="0";
}
//Gets the greatest common divisor.
$x=(int)$decarr[1];
$y=(int)$div;
$gcd=gmp_strval(gmp_gcd($x,$y));
//Calculates the result and fills the array with the correct sign.
if($neg){
$fraccion[0]=((abs($decarr[0])*($y/$gcd))+($x/$gcd))*(-1);
}else{
$fraccion[0]=(abs($decarr[0])*($y/$gcd))+($x/$gcd);
}
$fraccion[1]=($y/$gcd);
return $fraccion;
}
Just adding a bit more logic to Derek's accepted answer - check for "division by zero" and whole number input check.
function fractionToDec($input) {
if (strpos($input, '/') === FALSE) {
$result = $input;
} else {
$fraction = array('whole' => 0);
preg_match('/^((?P<whole>\d+)(?=\s))?(\s*)?(?P<numerator>\d+)\/(?P<denominator>\d+)$/', $input, $fraction);
$result = $fraction['whole'];
if ($fraction['denominator'] > 0)
$result += $fraction['numerator'] / $fraction['denominator'];
}
return $result;
}
function frac2dec($fraction) {
list($whole, $fractional) = explode(' ', $fraction);
$type = empty($fractional) ? 'improper' : 'mixed';
list($numerator, $denominator) = explode('/', $type == 'improper' ? $whole : $fractional);
$decimal = $numerator / ( 0 == $denominator ? 1 : $denominator );
return $type == 'improper' ? $decimal : $whole + $decimal;
}
Use a 3rd party library, for example:
https://packagist.org/packages/phospr/fraction
Usage:
$fraction = Fraction::fromFloat(1.5);
echo "Fraction is: " . $fraction->getNumerator() . '/' . $fraction->getDenominator();
echo "Float is: " . $fraction->toFloat();
I usually do a quick search on https://packagist.org to see if something already exists to solve what I'm trying to do, if so then I can take advantage of the many hours that the community have already put into solving the problem (this will be much much more time than I'll be able to dedicate to it) and it will also be more likely to be bug free, having been battle tested by others and maybe even have a test suite covering it.
Saves time and results in higher quality.