Kotlin functions: %, rem and mod
This is a short article I’m writing in 2022 for my future self, because I keep forgetting the difference between these functions and their precise behavior. Maybe you’ll also find it useful!
TL;DR for modern Kotlin (version 1.5 and later):
%
is the same asrem
,mod
is different%
/rem
will return 0 or a value with the same sign as the dividend
(number “on the top” of the fraction)mod
will return 0 or a value with the same sign as the divisor
(number “on the bottom” of the fraction)someList[n % someList.size]
can throwIndexOutOfBoundsException
(ifn
is negative)
Runnable example of the matrix of options:
The reason why I’m writing this article (and why I personally keep getting confused) is that this behavior was not always the case, as is revealed if you search the web for mod
vs rem
behavior in Kotlin or change the version of Kotlin in the playground above. The history here also reveals how long it takes to fundamentally redefine what a function does.
In the beginning (of Kotlin), %
was the same as the operator function mod
, and it had the same behavior that %
and rem
have today.
In Kotlin 1.1, the operator function mod
was deprecated with the replacement of rem
(commit). At this point, all 3 functions had the same behavior which matches the behavior of %
and rem
today… at least for built-in operations on numbers. Since %
is a special operator function, the language version of Kotlin you were targeting impacted whether mod
or rem
was actually called (this would be extremely important for a custom implementation with operator overloading)
In Kotlin 1.3, the deprecation level for the operator function mod
was raised from a warning to an error (commit).
This set the stage for Kotlin 1.4, which removed mod
completely for the numeric types (commit).
After they were removed for a full release, Kotlin 1.5 added new mod
functions for numeric types, finally resulting in the same behavior that we have today (commit). Most notably, the new mod
behaves differently than %
/rem
as showcased above, and therefore differently from the original mod
function as well. I also want to specifically call out this commit for greatly clarifying the docs of rem
and mod
.
And that brings us to the present day! If this is your first time stumbling upon these two functions with similar names and functionality, hopefully this helps explain the behavior you can expect from any recent Kotlin code, and how we got to where we are now.