40

I want to round a number and I need a proper integer because I want to use it as an array key. The first "solution" that comes to mind is:

$key = (int)round($number)

However, I am unsure if this will always work. As far as I know (int) just truncates any decimals and since round($number) returns a float with theoretically limited precision, is it possible that round($number) returns something like 7.999999... and then $key is 7 instead of 8?

If this problem actually exists (I don't know how to test for it), how can it be solved? Maybe:

$key = (int)(round($number) + 0.0000000000000000001) // number of zeros chosen arbitrarily

Is there a better solution than this?

9
  • 4
    Integers stored within floats are always accurate, up to around 2^51, which is much more than can be stored in an int anyway. You are worrying over nothing. Commented Aug 3, 2016 at 14:22
  • @NiettheDarkAbsol Oops, I actually knew that (from Javascript) but didn't make the connection. You should make that an answer. BTW, in JS it's 2^53-1 iirc. Commented Aug 3, 2016 at 14:23
  • When you use rounded numbers as key, you run in trouble if you have more then one 7.x numbers with the rounded result of 8. Commented Aug 3, 2016 at 14:25
  • @u-nik It's exactly my intention that 7.4 and 7.6 end up with the same key. :) Commented Aug 3, 2016 at 14:27
  • There are three functions to handle rounding numbers: round, [ceil][] (round up), [floor][] (round down). I'm unsure which one you want to use. [ceil]: secure.php.net/ceil [floor]: secure.php.net/floor Commented Aug 3, 2016 at 14:28

8 Answers 8

63

To round floats properly, you can use:

Those functions return float, but from Niet the Dark Absol comment: "Integers stored within floats are always accurate, up to around 2^51, which is much more than can be stored in an int anyway."

Sign up to request clarification or add additional context in comments.

1 Comment

The problem with these is that their return type is float, not int.
9

round(), without a precision set always rounds to the nearest whole number. By default, round rounds to zero decimal places.

So:

$int = 8.998988776636;
round($int) //Will always be 9

$int = 8.344473773737377474;
round($int) //will always be 8
 

So, if your goal is to use this as a key for an array, this should be fine.

You can, of course, use modes and precision to specify exactly how you want round() to behave. See this.

You might actually be more interested in intval:

echo intval(round(4.7)); //returns int 5
echo intval(round(4.3)); // returns int 4

3 Comments

Nope, intval(4.7) == 4, which is not what I want.
Yea, with intval by itself. Combine it with round. I've updated my answer
That suffers from the same (non-)problem that I described.
6

What about simply adding 1/2 before casting to an int?

eg:

$int = (int) ($float + 0.5);

This should give a predictable result.

1 Comment

why only three upvotes? I mean, yes, 2^51 as described in accepted answer is a lot, but still this technique is even more correct, right?
2

For My Case, I have to make whole number by float or decimal type number. By these way i solved my problem. Hope It works For You.

$value1 = "46.2";
$value2 = "46.8";


// If we print by round()
echo round( $value1 );    //return float 46.0
echo round( $value2 );    //return float 47.0


// To Get the integer value
echo intval(round( $value1 ));   // return int 46
echo intval(round( $value2 ));   // return int 47

1 Comment

Please comment if have any Issue with the code. will try to update for better understand.
1

Integers stored within floats are always accurate, up to around 253, which is much more than can be stored in an int anyway. I am worrying over nothing.

Comments

1

Update PHP 8.4

PHP 8.4 introduces new 3 functions for arbitrary precision rounding in extension BCMath: bcceil(), bcround(), bcfloor().

bcround('0.285', 2, \RoundingMode::HalfAwayFromZero); // string(4) "0.29"

bcfloor('13.0915'); // string(2) "13"

bcceil('24.00001'); // string(2) "25"

If you set up the precision to 0, you can easily cast the string to an integer without a floating-point error.

More information:

Moreover, you can make use of the new 4 rounding modes added to the standard library which can be used with the round() function and rounding functions from BCmath.

round(5.5, mode: \RoundingMode::HalfAwayFromZero); // 6
round(5.5, mode: \RoundingMode::HalfTowardsZero); // 5
round(5.5, mode: \RoundingMode::HalfEven); // 6
round(5.5, mode: \RoundingMode::HalfOdd); // 6

//introduced in PHP 8.4
round(5.5, mode: \RoundingMode::TowardsZero); // 5
round(5.5, mode: \RoundingMode::AwayFromZero); // 6
round(5.5, mode: \RoundingMode::NegativeInfinity); // 5
round(5.5, mode: \RoundingMode::PositiveInfinity); // 6

More information:

2 Comments

They all still return a float.
You are right. I forgot to put all changes.
-1

My solution:

function money_round(float $val, int $precision = 0): float|int
{
    $pow = pow(10, $precision);

    $result = (float)(intval((string)($val * $pow)) / $pow);
    if (str_contains((string)$result, '.')) {
        return (float)(intval((string)($val * $pow)) / $pow);
    }
    else {
        return (int)(intval((string)($val * $pow)) / $pow);
    }
}

1 Comment

Welcome to StackOverflow! Please provide an explanation for your code.
-3

Round to the nearest integer

$key = round($number, 0);

3 Comments

It returns a float.
How about $key = intval(round($number, 0)) or $key = (int)round($number, 0)
Did you even read my question? I specifically gave (int)round($number) as an example of what I thought is not enough.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.