Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby modulo 3 with negative numbers is unintuitive

Tags:

ruby

modulo

Ruby modulo rules with negative numbers are unclear. In IRB:

-7 % 3 == 2  

Should be 1! Why?

like image 511
Babar-Baig Avatar asked Apr 18 '13 03:04

Babar-Baig


2 Answers

Because -7/3 is -3 under Ruby's integer division semantics. 3*-3 is -9, so that would leave a remainder of 2.

According to the docs, x modulo y is defined as:

x-y*(x/y).floor
like image 120
Paul Prestidge Avatar answered Nov 14 '22 01:11

Paul Prestidge


When one of the operands to % is negative, there’s no clear best answer for what result to return. Every programming language has its own rules. The Wikipedia page for Modulo operation has a giant table of how each programming language has decided to handle this, and there’s no clear consensus:

$ # Modulus sign is:
$
$ curl 'http://en.wikipedia.org/w/index.php?title=Modulo_operation&action=edit&section=1' \
    | egrep -o 'Divisor|Dividend|Always positive|Closest to zero|Not defined|Implementation defined' \
    | sort | uniq -c | sort -nr

  67 Dividend
  42 Divisor
   7 Always positive
   4 Closest to zero
   2 Not defined
   2 Implementation defined

Some choose the sign of the left-hand operand, and some the right-hand operand. Others don’t specify. For example, The C Programming Language says:

the sign of the result for % [is] machine-dependent for negative operands

Instead of making a particular choice for how to handle this, C just returns whatever the particular hardware or compiler being used have chosen to implement! This seems to have been standardized in more recent versions of the C programming language standards.

If you want to get a particular version in Ruby, there are two different methods you could call, modulo aka %, and remainder, with different behaviour on negative numbers:

$ irb
irb(main):001:0> -7.modulo(3)
=> 2
irb(main):002:0> -7.remainder(3)
=> -1

In other languages that don’t have built-in methods for this, you may end up using % twice to get the desired sign.

like image 21
andrewdotn Avatar answered Nov 13 '22 23:11

andrewdotn