17

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..

php_nub_qq
  • 15,199
  • 21
  • 74
  • 144
  • 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 Jun 13 '14 at 12:49
  • possible duplicate of [PHP, How to catch a division by zero?](http://stackoverflow.com/questions/3071067/php-how-to-catch-a-division-by-zero) – Rikesh Jun 13 '14 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 Jun 13 '14 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. – Amal Murali Jun 13 '14 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. – Sergiu Paraschiv Jun 13 '14 at 12:51
  • @bartek: try...catch can be slow, if it needs to be checked many times – beerwin Jun 13 '14 at 12:56

8 Answers8

43

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. :)

Will B.
  • 17,883
  • 4
  • 67
  • 69
Ryan Vincent
  • 4,483
  • 7
  • 22
  • 31
  • 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 Nov 23 '17 at 19:20
  • 1
    This will haunt you later if defender->rep ever falls below 1 but is not 0 – Ron Jan 23 '18 at 20:16
  • @Ron, I agree - I suspect that if the rep can be a floating point number then there are other issues that would need to be considered.I didn't overthink the answer when I wrote it :) I was surprised it was accepted after a couple of years :) – Ryan Vincent Jan 23 '18 at 22:27
  • @RyanVincent There is also issue with negative integers. I believe matiit's answer is correct due to minimizing the affected space (only rep === 0, not rep < 1) and the explicit definition of the result – Ron Jan 24 '18 at 17:03
  • 1
    Thanks, it's a such an elegant way. – Shahrukh Anwar Oct 31 '19 at 11:44
  • 1
    your second "todays php" example does not work in php 7.4: https://3v4l.org/T5ddp `$y = 100/($x ?: 1);` is the correct way. – oceanBT Jun 03 '20 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. – Ryan Vincent Jun 03 '20 at 17:16
  • 1
    To continue the topic of the alternate solution, [`??` is the null coalescing operator](https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op) added in PHP 7.0+, which requires the operand to equate to `null` rather than the expected [`empty`](https://www.php.net/manual/en/function.empty.php) value as suggested by oceanBit. The issue with using `0 ?? 1` will persist in any version of PHP that supports `??` not just PHP 7.4. – Will B. Aug 03 '21 at 15:37
13

This is the way I would do it.

$defender->ratio = ($defender->rep === 0) ? 0 : $attacker->rep / $defender->rep;
matiit
  • 7,969
  • 5
  • 41
  • 65
  • 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. – Sergiu Paraschiv Jun 13 '14 at 12:53
  • 2
    This is short but gets messy very quickly. – Amal Murali Jun 13 '14 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. – MarcinWolny Jun 13 '14 at 12:54
  • 1
    I don’t see why `x / 0 == 0` makes any sense. – Lumen Jun 13 '14 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. – Sergiu Paraschiv Jun 13 '14 at 12:56
  • 1
    Guys, i don't know if 0 is a good answer here - i don't know the context - but that way is in my opinion readable and short. @MarcinWolny exactly - it's the right tool for the job. Why === instead of == ? I think it should be properly transformed - if it'd be '0' not 0 - doing math makes no sense.. – matiit Jun 13 '14 at 13:21
  • @matiit - think about double vs integer instead of string vs integer :) – MarcinWolny Jun 13 '14 at 13:34
  • @MarcinWolny You're right :) I didn't think about it. I assumed that it's somewhere converted from string (as result from DB) to float. – matiit Jun 13 '14 at 13:37
  • Ok, just to be sure I tested it - and in deed your code breaks if your $defender->rep is 0.0 – MarcinWolny Jun 13 '14 at 13:42
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
RedSparr0w
  • 467
  • 4
  • 11
  • 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 Dec 05 '19 at 16:07
4

Something like this?

if($defender->rep != 0){
    $attacker->ratio = $defender->rep / $attacker->rep;
}
Wayne Whitty
  • 19,513
  • 7
  • 44
  • 66
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.

beerwin
  • 9,813
  • 6
  • 42
  • 57
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.

Useless Intern
  • 1,294
  • 1
  • 10
  • 20
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;
}
Lumen
  • 3,554
  • 2
  • 20
  • 33
0

For prevent Error Message use Try

     try {
    return $a/$b;
  } catch (DivisionByZeroError $e) {
    echo 'Error DivisionByZeroErro';
  }