19

Much like this site, my current project has reputation and in the script that I'm working on I need to calculate the ratio between two users' reputations.

$attacker->ratio = $defender->rep / $attacker->rep;
$defender->ratio = $attacker->rep / $defender->rep;

In doing so I may occasionally end up with the divisor's reputation being 0, which sucks!

Obviously I can add a couple of checks, but I was wondering if a prettier solution hasn't been invented, something like @ infront, but I know that's not a good idea..

6
  • 1
    Why not just check to see if the divisor is zero and, if so, don't divide by it? Really kinda simple when you think about it.
    – John Conde
    Commented Jun 13, 2014 at 12:49
  • possible duplicate of PHP, How to catch a division by zero?
    – Rikesh
    Commented Jun 13, 2014 at 12:50
  • What do you want to do if the reputation of a user is 0? What should that user’s ratio be in this case?
    – Lumen
    Commented Jun 13, 2014 at 12:50
  • The most sane would be to check if the divisor is zero before doing the division. Otherwise, you could create a custom function for division like mydivide() and handle the zero case there. But I prefer the first option, though. Commented Jun 13, 2014 at 12:50
  • In your situation dividing by zero is a special case. Just like in math it has no "meaning". I'd say in this case you'd need to display a special message, right? An if condition is more expressive than anything else. Commented Jun 13, 2014 at 12:51

9 Answers 9

47

Assuming positive numbers are valid then ensure the lowest divisor value will be 1.

$defender->ratio = $attacker->rep / max($defender->rep, 1);

// --------------------------------------------

suggested code by someone else,

@php_nub_qq suggested alternate code...
In today's php

$defender->ratio = $attacker->rep / ($defender->rep ?? 1);

Alas, this code provided by @php_nub_qq does not work in PHP 7.4 ;-( see @OceanBt in the comments... I thank them for the correction! :)

so, Here I am maintaining code that I never was interested in. And now, is shown to be PHP version specific! Here is the correction...

$y = 100/($x ?: 1);

Why am I doing this?

  1. Notice my code still works fine! Avoid 'clever features' for production code.
  2. Because someone believes that have a 'better answer' doesn't mean they do!

I don't mind doing this maintenance of the code of someone else! This is the real world! We have to do this. I posted it because:
I really am trying to help programmers to learn.

// My thoughts about the 'improvement' to what I posted...

imo, that suggestion of yours isn't the same as my approach! I specifically used 'max' as it forces a limit on a range of numbers. You can nest the 'min' and 'max' functions also to force a limited range.

Your method is a 'selection' and not why I did the answer I did. :)

8
  • 3
    What if $defender->rep ends up between 0 and 1? If you got modifiers that push the defender rep into the range between 0 and 1, you'll have bugs. Of course, this stands only if you are certain that the rep value can have integer values only.
    – beerwin
    Commented Nov 23, 2017 at 19:20
  • 1
    This will haunt you later if defender->rep ever falls below 1 but is not 0
    – Ron
    Commented Jan 23, 2018 at 20:16
  • 1
    Thanks, it's a such an elegant way. Commented Oct 31, 2019 at 11:44
  • 1
    your second "todays php" example does not work in php 7.4: 3v4l.org/T5ddp $y = 100/($x ?: 1); is the correct way.
    – oceanBT
    Commented Jun 3, 2020 at 13:25
  • 1
    @oceanBT, thanks for the update!, Appreciated. I am stating to suspect that I should have just disallowed the edit to my post and linked to the person who made the post. whatever, Thanks to the correction and applied. Commented Jun 3, 2020 at 17:16
13

This is the way I would do it.

$defender->ratio = ($defender->rep === 0) ? 0 : $attacker->rep / $defender->rep;
9
  • 2
    I hate one liners like this one. That syntax as it makes handling the special case unreadable AND you still need an if condition after. Commented Jun 13, 2014 at 12:53
  • 2
    This is short but gets messy very quickly. Commented Jun 13, 2014 at 12:54
  • 4
    You might hate it, but this kind of syntax was specifically made to solve problems like the one op has. And if done right - you won't need any additional if statements. Commented Jun 13, 2014 at 12:54
  • 1
    I don’t see why x / 0 == 0 makes any sense.
    – Lumen
    Commented Jun 13, 2014 at 12:56
  • 2
    Take his problem in context. Is displaying 0 OK here? I'd say telling the user "Not enough info to compare" is better. Commented Jun 13, 2014 at 12:56
10

If you are using PHP > 5.3 you could use the ternary operator ?:

$attacker->ratio = $defender->rep / ($attacker->rep ?: 1);
$defender->ratio = $attacker->rep / ($defender->rep ?: 1);

This means if the variable before the operator is false or empty the value afterwards would be returned otherwise the first value is returned

Example below:

$attacker->rep = 0;
$defender->rep = 55;
$attacker->ratio = $defender->rep / ($attacker->rep ?: 1); // returns 55 / 1 = 55
$defender->ratio = $attacker->rep / ($defender->rep ?: 1); // returns 0 / 55 = 0
1
  • 1
    This should be the accepted answer as it does not force the divider to be positive! By using max() which is the accepted answer, you miss out on using negative numbers as dividers.
    – Eugenio
    Commented Dec 5, 2019 at 16:07
4

Something like this?

if($defender->rep != 0){
    $attacker->ratio = $defender->rep / $attacker->rep;
}
2

Use a ternary operator to check if the divider is zero or not.

$attacker->ratio = $attacker->rep > 0 ? $defender->rep / $attacker->rep : 1;
$defender->ratio = $defender->rep > 0 ? $attacker->rep / $defender->rep : 1;

Use whatever you wish instead of the value 1 as default.

2
  • You are discarding negative numbers here.
    – colburton
    Commented Jun 13, 2014 at 12:53
  • 1
    It's an example. I don't know how the game mechanics are working in this case.
    – beerwin
    Commented Jun 13, 2014 at 12:54
2

Based upon the other answers I'll assume you only update the ratio if it's not zero (Remember you asked for elegance not clarity):

if( !empty($attacker->rep) ) { 
    $attacker->ratio = $defender->rep / $attacker->rep;
}

PHP treats 0 as empty.

1

I don’t recommend to change any user’s reputation artificially solely to make the math work. Instead you can do this:

function calc_rep_ratio($self, other)
{
    if ($self->rep != 0) {
        return $other->rep / $self->rep;
    } else {
        return NAN;
    }
}

Then use the function

$defender->ratio = calc_rep_ratio($defender, $attacker);
$attacker->ratio = calc_rep_ratio($attacker, $defender);

In the presentation, you can check for the number

if (is_nan($user->ratio)) {
    echo 'No ratio available';
} else {
    echo $user->ratio;
}
0

For prevent Error Message use Try

     try {
    return $a/$b;
  } catch (DivisionByZeroError $e) {
    echo 'Error DivisionByZeroErro';
  }
1
  • only in php>=8.0
    – woens
    Commented Jan 30, 2023 at 10:29
0

We faced the same issue on our legacy PHP Application. Our team fixes it with two functions:

    function ensure_num($val): float|int
    {
        if (is_numeric($val)) {
            return (float) $val;
        }

        return 0;
    }

    function x_divide(mixed $num1, mixed $denominator): float
    {
        $num1 = ensure_num($num1);
        $denominator = ensure_num($denominator);
        if ($denominator == 0) {
            return 0;
        }

        return $num1/$denominator;
    }

Then you don't need to worry anymore about this warning on PHP version 8.0 and possible a fatal error on version 8.3.

To use it, just replace:

$var1 = $num1/$num2;

to:

$var1 = x_divide($num1, $num2);

Using the above makes your legacy to not throw any issue anymore.

Not the answer you're looking for? Browse other questions tagged or ask your own question.