8

When naively using the mod command in bash the residual gets the wrong sign (in my opinion) for negative numerators:

If i write:

for i in {-5..5}; do echo $(( $i % 3 )) ; done

i get the output (as a row)

-2 -1 0 -2 -1 0 1 2 0 1 2

How do i achieve the "correct" behavior

1 2 0 1 2 0 1 2 0 1 2
Mikael Fremling
  • 720
  • 2
  • 8
  • 24
  • 2
    Related: [modulo operator with negative operand, bug or feature?](http://www.unix.com/shell-programming-and-scripting/260045-newbie-question-modulo-operator-negative-operand-bug-feature.html) – fedorqui Jan 20 '17 at 14:40

3 Answers3

6

I know it's an old question, but rather than loop until the result is positive or launch perl or python consider the following:

for i in {-5..5}; do echo $(( (($i % 3) + 3) % 3)) ; done

this will result in the OP's desired output.

This works because the first modulo will bring the result into the range -3 to 3, adding 3, causes the result to be in the range 0 to 6, we can then perform modulo again (adding 3 has no effect on this).

in general: mod = ((a % b) + b) % b

Jonathan Cowling
  • 525
  • 1
  • 5
  • 19
3

Add 3 and then Mod 3 to the first set of results:

$ for i in {-5..5}; do printf "%d " $(( (($i % 3) + 3) % 3 )) ; done
1 2 0 1 2 0 1 2 0 1 2

If you know the maximum range, you can just add a significantly large enough multiple of 3 to make all numbers positive before the first modulo operation.

$ for i in {-5..5}; do printf "%d " $(( ($i + 3000000) % 3 )) ; done

However, the first approach is cleaner and more universal.

Lastly, for fun:

positive_mod() {
  local dividend=$1
  local divisor=$2
  printf "%d" $(( (($dividend % $divisor) + $divisor) % $divisor ))
}

for i in {-5..5}; do
  printf "%d " $(positive_mod $i 3)
done
Harvey
  • 5,703
  • 1
  • 32
  • 41
  • 1
    In general, you can add 3 to the numerator until the result is positive, *then* divide by 3. `(j + k*3) % 3 == j % 3` for all `k`. – chepner Jan 20 '17 at 14:50
  • 1
    @chepner: You can do that, but you can never really know how big `k` needs to be. If you do that whole operation after the first modulus, then we know that `k=1`. – Harvey Jan 20 '17 at 14:53
  • 1
    Unless I am mistaken, you can remove the innermost parentheses and go with $(( ($i % 3 + 3) % 3 )) due to operator precedence. – Fred Jan 20 '17 at 15:06
  • Although I don't quite like it. I suppose this is the best way to go. – Mikael Fremling Jan 20 '17 at 15:11
  • @Fred: I think you're right. I was going for clarity over brevity. – Harvey Jan 20 '17 at 15:23
3

According to wikipedia negative signs are allowed.

[The result of a mod n] this still leaves a sign ambiguity if the remainder is nonzero: two possible choices for the remainder occur, one negative and the other positive, and two possible choices for the quotient occur. Usually, in number theory, the positive remainder is always chosen, but programming languages choose depending on the language and the signs of a or n.

So it's up to the programming language to define this. As bash has obviously gone for the "negative remainder" way you might escape to e.g. perl like this:

for i in {-5..5}; do perl -le "print $i%3"; done

This is at the cost of launching the Perl interpreter individually for each integer.

Indeed! Since the OP seem to care about correct math, you might consider switching to something like python and do the looping and everything in there.

hansaplast
  • 11,007
  • 2
  • 61
  • 75
  • 1
    This is at the cost of launching the Perl interpreter individually for each integer. It works, but I think the slightly longer solution avoiding Perl is worth the performance and avoiding an external dependency. – Fred Jan 20 '17 at 15:08
  • When i say wrong, i'm taking the perspective of number theory. – Mikael Fremling Jan 20 '17 at 15:09