Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get positive sign for result from mod in bash

Tags:

bash

mod

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
like image 917
Mikael Fremling Avatar asked Jan 20 '17 14:01

Mikael Fremling


People also ask

What does [- Z $1 mean in bash?

$1 means an input argument and -z means non-defined or empty. You're testing whether an input argument to the script was defined when running the script. Follow this answer to receive notifications.

Is Result of Modulo always positive?

Is modulus always positive? The answer is “Yes”. Reason: The value of modulus of any number is always positive.

What does %% mean in bash?

So as far as I can tell, %% doesn't have any special meaning in a bash function name. It would be just like using XX instead. This is despite the definition of a name in the manpage: name A word consisting only of alphanumeric characters and under- scores, and beginning with an alphabetic character or an under- score.

How do I use bash mod operator?

To use modulo in the shell, we have to utilize the “expr” command to evaluate its value. So, we have consecutively added three “expr” commands to find out the modulo of two integer values each time by using the “%” operator between them and got three remainder values.


3 Answers

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

like image 103
Jonathan Cowling Avatar answered Nov 09 '22 03:11

Jonathan Cowling


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
like image 33
Harvey Avatar answered Nov 09 '22 03:11

Harvey


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.

like image 31
hansaplast Avatar answered Nov 09 '22 03:11

hansaplast