Ruby way: catch division by zero

18,693

Solution 1

You can use nonzero?, as in:

def compute_average(a,b,c,d,e)
  total = [a,b,c,d,e].sum.to_f
  average = [a, 2*b, 3*c, 4*d, 5*e].sum / (total.nonzero? || 1)
end

More people would be more familiar with using the ternary operator (total == 0 ? 1 : total), so that's another possibility.

Solution 2

It's common to rely on rescue to capture the exception, then to return the default value:

def compute_average(a, b, c, d, e)
  total = [a, b, c, d, e].sum.to_f
  average = [ a, 2*b, 3*c, 4*d, 5*e ].sum / total
  average.round(2)
  rescue ZeroDivisionError
    0.0
end

Also I'd write:

average = numerator / denominator unless denominator == 0 then 0

as

average = (denominator == 0) ? 0 : numerator / denominator

Solution 3

While this is an outdated thread I thought I would chime in with a simple one liner you can use...

@average = variable1 / variable2 rescue 0

Solution 4

def compute_average(a,b,c,d,e)
  total = (a+b+c+d+e).to_f
  total.zero? ? 0 : ((a + 2*b + 3*c + 4*d + 5*e) / total).round(2)
end

Solution 5

To me, the cleanest way is:

numerator / denominator rescue 0

It also saves you from handling 0 / 0.

As @Andrew points out, this is only valid for integers. See the comments to this answer for more info.

Share:
18,693

Related videos on Youtube

Andrew
Author by

Andrew

Polyglot engineer and people leader.

Updated on December 16, 2020

Comments

  • Andrew
    Andrew over 3 years

    I have the following method to compute an average:

    def compute_average(a,b,c,d,e)
      total = [a,b,c,d,e].sum.to_f
      average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
      average.round(2)
    end
    

    It's nothing special, but it has a problem that I expect all average equations have: it might divide by zero if inputs are all zero.

    So, I thought of doing this:

    def compute_average(a,b,c,d,e)
      total = [a,b,c,d,e].sum.to_f
      if total==0
        average = 0.00
      else
        average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
        average.round(2)
      end
    end
    

    ... and that works, but it feels kludgy to me. Is there a more elegant, "Ruby Way" to avoid this division by zero problem?

    What I'm wishing I had was an "unless then" operator, like...

    average = numerator / denominator unless denominator == 0 then 0
    

    Any suggestions?

    • Mat
      Mat
      that's a strange average function. more normal averages (arithmetic/geometric) divide by the number of elements, so they don't really have that problem unless you try to take the average of an empty set.
    • Jakub Hampl
      Jakub Hampl
      Yeah rails adds it in ActiveSupport. It's easy to add yourself though reduce 0, &:+.
  • Andrew
    Andrew about 13 years
    I like this and also Jakub's method, but yours keeps the assignment at the front -- which I realize isn't really necessary, but which I like because it helps me remember what I'm doing when I look at this later on.
  • tokland
    tokland about 13 years
    wouldn't the ternary operator be more idiomatic here?
  • Jakub Hampl
    Jakub Hampl about 13 years
    This can give different results in the case of compute_average(1,0,0,0,-1) #=> -4 whereas @Andrew's and mine will give 0 at that point.
  • tokland
    tokland about 13 years
    A bit too subtle... it relies on the fact that the numerator is also 0 when total is 0, which is an orthogonal fact. A plain usage of the ternary operation would seem more clear: average = (total.zero? ? 0 : [a, 2*b, 3*c, 4*d, 5*e].sum / total).round(2)
  • tokland
    tokland about 13 years
    Yup, the double question mark isn't visually nice (and you always forget to write the second one!). But I guess at the end you just get use to it.
  • Jakub Hampl
    Jakub Hampl about 13 years
    This function is only correct when assuming positive input - which considering it's counting stars may be a sound assumption.
  • Andrew
    Andrew about 13 years
    That's a good point - although in this case there's no possibility of negative input. Thanks for the tip!
  • Andrew
    Andrew over 10 years
    Just checking, if you use rescue in postfix it will only save the current expression? ie. would this work? a = raise 'not this' rescue 'this'; a #=> 'this'
  • Andrew
    Andrew over 10 years
    So, just fiddling with this, if one of your args is a float, then this won't work as it will return infinity, so this is only viable if you're doing integers. With division I figure you're often going to be interested in fractional results, so floats are likely to be part of the equation. Nice idea, though!
  • TJChambers
    TJChambers over 9 years
    I just got burn't by using this technique on a float. After the division it returns NaN rather that triggering the rescue. Grrr.
  • Nate
    Nate over 9 years
    nonzero? is on the Numeric class, so if denominator is nil this will still throw an error.
  • Nate
    Nate over 9 years
    This is extremely inefficient if the rescue is triggered more than 5% of the time and it can hide other errors making debugging difficult. (Sorry about the delete and commend. I accidently pated the Google search URL.)
  • Nate
    Nate over 9 years
    This function will still raise an exception if any parameter is nil because sum raises TypeError: NilClass can't be coerced into Fixnum in this case.

Related