Modulo
February 26, 2023 at 10:26 AM by Dr. Drang
I left something out of yesterday’s post. Another mathematical aspect of Underscore David Smith’s unitSquareIntersectionPoint
function is this set of lines at the top of the function:
var normalizedDegree = angle.degrees
while normalizedDegree > 360.0 {
normalizedDegree -= 360.0
}
while normalizedDegree < 0.0 {
normalizedDegree += 360.0
}
There are lots of ways to normalize an angle. Here, UDS wants to take the input angle
and turn it into the equivalent angle between 0° and 360°. If angle
is already in that range, neither of the while loops is entered and normalizedDegree
remains equal to the input angle
.
You may recognize this as an example of modular arithmetic extended to include floating point numbers. SSteve did, and UDS included their code that makes that connection more explicit:
var normalizedDegree = angle.degrees % 360;
normalizedDegree = normalizedDegree < 0 ? normalizedDegree + 360 : normalizedDegree;
Unfortunately, SSteve wasn’t able use Swift’s %
operator alone, because it returns a value with a sign that matches the sign of the dividend. Thus the second line with the ternary operator was needed to ensure a positive result.
Sign ambiguity can be a nasty problem in modular arithmetic because different programming languages treat modular arithmetic differently. If I had to normalize the input angle in my xy
function to get it between 0° and 360°—which I don’t because the trig functions handle angles outside that range correctly—I’d be able to use
python:
normalizedAngle = angle % 360
in Python without further adjustment. Python uses a floored division definition of %
, so it always returns a value with the same sign as the divisor—in this case, +360.
When I read Underscore’s first post, I didn’t know whether Swift even had a modulo operator or how it worked. I went to the Wikipedia page looking for an answer and found three:
%
uses truncated division, which, as we’ve seen, returns an answer with the same sign as the dividend. Unfortunately, it’s limited to integers, so it probably shouldn’t be used in USD’s function (unless there’s some typecasting going on in SSteve’s code that I don’t understand).truncatingRemainder(dividingBy:)
, like%
, uses truncated division, but it can be used with floating point numbers. This is probably what should be used in SSteve’s code snippet.remainder(dividingBy:)
uses rounded division, where the sign depends on how close the dividend is to an integer multiple of the divisor. The sign can be either positive or negative, which I hate.
So none of these can be used without an adjustment.
Modular arithmetic doesn’t come up often in my work, but when it does, I get nervous about the sign of the result. I’ve programmed in too many languages over the years to remember what type of division is used for the modulo operator in the language I’m currently writing in. Even the language I use the most, Python, has different ways of doing modulo arithmetic. Many different ways:
- The modulo operator,
%
, uses floored division. - The
divmod
function uses floored division. - The
fmod
function in themath
library uses truncated division. - The
remainder
function in themath
library uses rounded division. - The
mod
function in the NumPy library uses floored division. - The
fmod
function in the NumPy library uses truncated division. - The
remainder
function in the NumPy library uses floored division.
This is the sort of thing that makes you think Underscore was right in writing his own modular arithmetic code. It may take up more space, but at least the way it works is obvious.