0

I need to do integer division. I expect the following to return 2 instead of the actual 1:

187 / 100 # => 1

This:

(187.to_f / 100).round # => 2

will work, but does't seem elegant as a solution. Isn't there an integer-only operator that does 187 / 100 = 2?


EDIT

I'll be clearer on my use case since I keep getting down-voted:

I need to calculate taxes on a price. All my prices are in cents. There is nothing below 1 cent in the accountability world so I need to make sure all my prices are integers (those people checking taxes don't like mistakes... really!)

But on the other hand, the tax rate is 19%.

So I wanted to find the best way to write:

def tax_price(price)
  price * TAX_RATE / 100
end

that surely returns an integer, without any floating side effect.

I was afraid of going to the floating world because it has very weird side-effects on number representation like:

So I found it safer to stay in the integer or the fractional world, hence my question.

Community
  • 1
  • 1
Augustin Riedinger
  • 20,909
  • 29
  • 133
  • 206

8 Answers8

10

You can do it while remaining in the integer world as follows:

def round_div(x,y)
  (x + y / 2) / y
end

If you prefer, you could monkey-patch Fixnum with a variant of this:

class Fixnum
  def round_div(divisor)
    (self + divisor / 2) / divisor
  end
end

187.round_div(100)    # => 2
pjs
  • 18,696
  • 4
  • 27
  • 56
  • Wouldn't `Integer` be the better choice than only `Fixnum`? – cremno Apr 28 '16 at 14:48
  • @cremno That's an easy substitution if you feel that's a better location. – pjs Apr 28 '16 at 15:01
  • Note that if `y` (or `divisor`) is assigned a float, the answer will be incorrect. In real life the method should confirm that `y` (or `divisor`) is an integer. – Cary Swoveland Apr 28 '16 at 18:19
  • `Fixnum` isn't guaranteed to exist. It's an implementation-specific private internal implementation detail that should never have leaked to the programmer. (Similar to flonums in YARV, which are the exact same thing for floating point numbers, but implemented inside YARV instead of exposing them to the programmer.) There is an issue on the Ruby bugtracker where the Ruby designers admit that it was a mistake and state that `Fixnum` and `Bignum` will be deprecated and removed in a future version of YARV and Ruby. – Jörg W Mittag Apr 28 '16 at 21:14
8

No – (a.to_f / b.to_f).round is the canonical way to do it. The behavior of integer / integer is (for example) defined in the C standard as "discarding the remainder" (see http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf page 82) and ruby uses the native C function.

Matthias Winkelmann
  • 15,870
  • 7
  • 64
  • 76
5

This is a less know method, Numeric#fdiv

You use it like this : 187.fdiv(100).round

Alfie
  • 2,706
  • 1
  • 14
  • 28
3

Not sure, but this might be what you have in mind.

q, r = 187.divmod(100)
q + (100 > r * 2 ? 0 : 1) # => 2
sawa
  • 165,429
  • 45
  • 277
  • 381
1

This should work for you :

Use syntax like this.

(number.to_f/another_number).round

Example:

(18.to_f/5).round
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
VijayGupta
  • 637
  • 3
  • 11
0

As @MattW already answer (+1), you'd have to cast your integers to floats.

The only other way that is less distracting can be to add .0 to your integer:

(187.0 / 100).round

However, usually we don't operate on concrete integers but variables and this method would be no use.

Uzbekjon
  • 11,655
  • 3
  • 37
  • 54
0

After some thoughts, I could:

  • have used BigDecimals but it feels like a bazooka to kill a bird

  • or I can use a custom method that wouldn't use floating division within the process, as @sawa suggests

    def rounded_integer_div(numerator, denominator) q, r = numerator.divmod(denominator) q + (100 > r * 2 ? 0 : 1) end

Augustin Riedinger
  • 20,909
  • 29
  • 133
  • 206
0

If what you want is to actually only increase the result by 1 if there's any remainder (e.g. for counting paging/batching), you can use the '%' (modula operation) for remainders checking.

# to add 1 if it's not an even division
a = 187
b = 100
result = a / b #=> 1
result += 1 if (a % b).positive?
#=> 2

# or in one line
result = (a / b) + ((a % b).zero? ? 0 : 1)
VinnyQ77
  • 385
  • 2
  • 10