4

Is there a way to round the result of integer division up to the nearest integer, rather than down?

For example, I would like to change the default behavior:

irb(main):001:0> 5 / 2
=> 2

To the following behavior:

irb(main):001:0> 5 / 2
=> 3
Sculper
  • 756
  • 2
  • 12
  • 24
ray
  • 641
  • 2
  • 8
  • 13

3 Answers3

3

This is rather an algorithm question than a ruby specific question.
Try (a + b - 1) / b. For example

(5 + 2 - 1) / 2  #=> 3
(10 + 3 - 1) / 3  #=> 4
(6 + 3 - 1) / 3  #=> 2

You can define an instance method, say divide_by, in the Integer class (monkey patch):

class Integer
  def divide_by(divisor)
    (self + divisor - 1) / divisor
  end
end

According to my benchmark result, it's about 1/2 times faster than the to_f then ceil solution.

CORRECTION

The method shown above gives wrong result when both the dividend and the divisor are negative.

Here's the method that gives the correct result in all cases: (a * 2 + b) / (b * 2)

a = 5
b = 2
(a * 2 + b) / (b * 2)  #=> 3

a = 6
b = 2
(a * 2 + b) / (b * 2)  #=> 3

a = 5
b = 1
(a * 2 + b) / (b * 2)  #=> 5

a = -5
b = 2
(a * 2 + b) / (b * 2)  #=> -2 (-2.5 rounded up to -2)

a = 5
b = -2
(a * 2 + b) / (b * 2)  #=> -2 (-2.5 rounded up to -2)

a = -5
b = -2
(a * 2 + b) / (b * 2)  #=> 3

a = 10
b = 0
(a * 2 + b) / (b * 2)  #=> raises ZeroDivisionError

The monkey patch should be

class Integer
  def divide_by(divisor)
    (self * 2 + divisor) / (divisor * 2)
  end
end

Mathematical Proof:

The dividend a and the divisor b meets the equation a = kb + m where a, b, k, m are all integers, b is not zero, and m is between b and 0 (can be 0).

For example, when a is 5 and b is 2, then a = 2b + 1, thus in this case, k = 2 and m = 1.

Another example for negative divisor, a is 5, b is -2, then a = -3b + (-1), thus k = -3 and m = -1.

(2a + b) / 2b
= (2(kb + m) + b) / 2b
= (2kb + b + 2m) / 2b

When m = 0

(2kb + b + 2m) / 2b
= (2k + 1)b / 2b
= k + (1 / 2)
= k + 0  # in Ruby
= k  # in Ruby

and since k = a / b, we got the correct answer.

When m is not 0,

(2kb + b + 2m) / 2b
= ((2k + 2)b - b + 2m) / 2b
= (k + 1) + (2m - b) / 2b

If b > 0, then 2m - b < b so (2m - b) / 2b < 1 / 2. So the second term is always 0 in integer division.

If b < 0, then 2m - b > b and still (2m - b) / 2b < 1 / 2 so the second term is still 0.

In either case, (2a + b) / 2b is rounded to k + 1 when m is not 0.

Aetherus
  • 8,720
  • 1
  • 22
  • 36
  • Just add the divisor to the dividend, and subtract by 1, then divide the result by the divisor. – Aetherus Aug 21 '15 at 16:17
  • That's a very interesting algorithm. Is there a proof that it works for all cases? – Toby 1 Kenobi Jun 03 '19 at 17:08
  • @Toby1Kenobi Sadly this method fails to deliver the expected result when both `a` and `b` are negative. I'm working on a solution now. – Aetherus Jun 04 '19 at 02:12
  • Awesome! If I were designing a programming language then it would definitely feature a rounded up intended division, with this formula behind it. – Toby 1 Kenobi Jun 04 '19 at 13:20
2

The function you are looking for is ceil.

Ceil returns the nearest integer, rounded upwards, for a floating point number.

4/3 = 1
4.0/3.0 = 1.3333...3
(4.0/3.0).ceil = 2

Also, note that this rounds in the positive direction, so

(-4.0/3.0).ceil = -1, NOT -2
  • Also, there is the corresponding floor function which rounds downwards.
mcfinnigan
  • 11,442
  • 35
  • 28
  • 1
    You can also write `4.fdiv(3).ceil` – Stefan Aug 21 '15 at 16:36
  • 1
    This will fail for certain inputs due to floating point inaccuracy. Aetherus's answer will not. There's probably a better example, but I tried 34524123126231231213535212/34524123126231231213535211. Your answer returns 1 due to the floating point format truncating the least significant digits. Aetherus's correctly returns 2. – Max Aug 21 '15 at 17:13
  • What you mean is that, for integers `x` and `y`, to divide `x` by `y`, rounding up, you can write `(x.to_f/y).ceil`, but that's what you say. You only refer to floating-point division. The second way has nothing to do with floats. It's simply `-(-x/y)`. The last sentence is irrelevant and, considering that you have given two ways, and there are others, the first is out-of-place. While writing this, I see someone downvoted, probably for the poor exposition. – Cary Swoveland Aug 21 '15 at 17:24
  • @Max, that's a valid point, but not necessarily a weakness of the methods, depending on the application. The methods may be perfectly fine if not calculating distances to far-away galaxies. A proviso is called for in the answer, however. – Cary Swoveland Aug 21 '15 at 17:38
  • I finally got around to computing the limit: 17952053266956643 is the largest integer that works with this method (though that might be implementation-dependent) in the sense that `x.to_f/(x-1).to_f` is not exactly `1.0` – Max Aug 22 '15 at 02:33
0

If what you actually want is to Integer div and round up if there's any left over, just do it straightforward as the logic would dictate on paper using a second line of modular operation (%) to check the remainders of the division:

a = 5
b = 2

result = a  / b #=> 2
result += 1 if (a % b).positive?

#=> 3

a = 6
b = 3
result = a  / b #=> 2
result += 1 if (a % b).positive?

#=> 2
VinnyQ77
  • 385
  • 2
  • 10