'Double modulo' operator

While this is an important thing to consider, it is a bit of an exaggeration. I'm sure that this change wouldn't break all existing code that uses %. I'd bet a large portion of the code using % is designed to only be used with positive numbers and so they technically wouldn't be broken by this change.

Other than that though, I do agree that the current implementation of % will most likely never be changed, but that a .mod or .modulo function would be a worthwhile inclusion. I do like the %% spelling, but I'm not attached to needing an operator for it.

Very few people write a / or a % into there code with the intention of performing a truncated division, or even the understanding of what answer it will produce when given negative numbers. Euclidean division is division as a human would do it, and it gives what most people would expect the result to be if they actually considered negative numbers, which most people don't. Most people are only peripherally aware that negative numbers exist at all.

Obviously, / would have to change alongside %. Making the two inconsistent with each other would be awful to work with.

Floored division only produces regular remainders when the divisor is positive, which, admittedly, is usually easier to control than the dividend. When given a negative divisor it has the same problems as truncated division.

With positive divisor:

Truncating Floored Euclidean
-4 % 4 0 0 0
-3 % 4 -3 1 1
-2 % 4 -2 2 2
-1 % 4 -1 3 3
0 % 4 0 0 0
1 % 4 1 1 1
2 % 4 2 2 2
3 % 4 3 3 3
4 % 4 0 0 0

With neagative divisor:

Truncating Floored Euclidean
-4 % -4 0 0 0
-3 % -4 -3 -3 1
-2 % -4 -2 -2 2
-1 % -4 -1 -1 3
0 % -4 0 0 0
1 % -4 1 -3 1
2 % -4 2 -2 2
3 % -4 3 -1 3
4 % -4 0 0 0

The worst case is that the code that actually wanted a truncated division, would have to use either use "do it like C does" &% or &/ operators (assuming those names pass muster), or use named methods: truncatedDivision(by:), truncatedRemainder(dividingBy:), etc.

You could also have a property on a SignedInteger & BinaryInteger that converts its value into a wrapper that implements the kind of division you want; e.g. let x = y.truncated % 5, but that's probably too magical.

The people who do care what kind of division they use are also largely the kind of people that read documentation, so the only hard part of the transition would be letting them know that the documentation had changed. We wouldn't really want to put warnings everywhere, because most people don't need truncated division.

Having said that, it may well be too source breaking to actually replace the default division operators, but I personally feel that making division truncated by default was a mistake even when previous programming languages did it, and merely considering using euclidean division would be a huge improvement when most languages pretend that floored and truncated are the only possible options.

The “Floored” column in your "With negative divisor:" table seems to be wrong, those remainders should be zero or have the same sign as the divisor. Has been fixed.

1 Like

I generated the results using gforth, it's possible that the Forth standard uses a different definition of "floored" integer division than everyone else is using. I'll double check the definitions and try again. Ah, I made a copy paste error. Fixed.

1 Like

This is highly non-obvious at best. By focusing on the remainder, you are sweeping considerable weirdness in the quotient under the rug. For example, "most people" expect that:

  1. (-a)/b == a/(-b) == -(a/b)
  2. (-a)/(-b) == a/b

and perform algebraic simplifications under the belief that these properties hold. Property (1) is satisfied only by unbiased rounding rules for the quotient. So nearest and truncating division satisfy it, but floor, ceiling, and Euclidean division do not. Property (2) is satisfied by any quotient-dominant rule, so everything except for Euclidean division meets it.

All of which is to say that there's no obviously right choice. There are perfectly reasonable arguments in favor of multiple options. Personally, I come down with Knuth (in favor of flooring division), but only very weakly. I can stomach the relatively stable status quo around / and % implementing truncating division, so long as other options are made available.

4 Likes

I agree (that it was a mistake not to include all three variants, and that it's too source breaking to change the semantics of %).

Also, note that changing the behavior of % would also break code for people (like me) who needed the floored or euclidean definition, and rolled their own, perhaps based on Swift's %.


Regarding how people (mis)use Swift's %. The following are some results of a quick search I did to see if I could find any examples of the common pitfall mentioned in the wikipedia article:

1:

let isOdd : (Int) -> Bool = { $0 % 2 == 1 }

This will return false for every negative number.

Found here: Transducers & Reducers in Swift 2 · GitHub


2:

func isOdd(number: Int) -> Bool {
    if number % 2 == 1 {
        return true
    } else {
        return false
    }
}

Same thing but more verbose.

Found here: Chapter 7: Functions - We ❤ Swift


3:

For example, imagine you wanted to compute a sum similar to that of triangle numbers, but only for odd numbers:

let count = 10
var sum = 0
for i in 1...count where i % 2 == 1 {
    sum += i
}

Not strictly wrong since i is in the range 1 ... count, but it must be considered bad practice to not write i % 2 != 0 instead, and it makes it seem like they misunderstand Swift's %.

Found here: Swift Tutorial Part 3: Flow Control | Kodeco


4:

The remainder operator is most useful when checking the parity, or “evenness,” of a number. If we wanted to know if a number x is even, we can use the remainder operator and check if the result is 0 or 1: x % 2 . If that result is 0, then we know that the number is even; if the result is 1, then we know the number is odd.

They recommend a method to check for odd numbers that will not work for negative numbers. If they had known how % worked, they would have wrote: "if the result is not 0, then we know the number is odd".

Found here: https://swiftludus.org/how-to-use-operators-in-swift/


My guess is that the % operator is widely misunderstood and misused (not just in Swift).

Including methods for all three (truncated, floored, euclidean) in the standard library would not only provide us with frequently needed functionality, but also help increase the awareness of their existence and differences.

10 Likes

Why would normal people expect that? Normal people tend to treat algebra as, at best, a chore to start with, and second neither of those hold when you do long division by hand.

No one on a discussion forum for the evolution of a programming language is likely to be a normal person, btw. We're all mad weirdos here.

They both hold for long division as taught in most US public schools. E.g.:

Take note of the signs of the two numbers. If both signs are positive or both are negative, the resulting figure will be positive. If only one of the signs is negative, you will end up with a negative number. As an example, 78 divided by -5 would give you a negative quotient. You can safely ignore the negative sign, as long as you remember the final outcome will be negative.

I'm not familiar with how it's taught outside of the US.

Well, I was homeschooled, so I suppose my understanding of long division is also abnormal.

1 Like

The even odd proposal would really benefit by adding this to its correctness argument I imagine. There's a couple of very popular sources listed here.

1 Like

I made the same mistake for isOdd in the initial version of that post :blush:

5 Likes

How about deprecating %?
(The one for ints that is, the one for floating point numbers is according to ieee754 iirc.)

The % operator mean different things in different languages, like Python and C. And my feeling is that anyone who would be upset if it was deprecated (and replaced with methods that cover all three division definitions) probably didn't really understand how it worked anyway.
: )

1 Like

I removed it years ago:
error: '%' is unavailable: For floating point numbers use truncatingRemainder instead

three division definitions

Oh, we're up to at least 5 now ... :joy:

1 Like

Oh, right, I forgot. Anyway, I think the one for integers is almost as problematic and should probably be deprecated too. IMO it would be more useful in the form of a similar error message than as a working operator (at least with the current behavior and syntax).

I just tried to search github for swift code using % but I didn't manage to get any results. It would be interesting to see how people are using it. Especially how many use cases there are that depends on, or even expects, its behavior for negative dividend or divisor.

I'm sympathetic, but it's quite a bit higher of a bar to deprecate things now than it was two years ago.

1 Like

i barely remember how to do long division anyway and i suspect a lot of people don’t either so i don’t really think this is that important a factor

1 Like

Afaics, no one here questions that either of the two missing modulo variants is more useful than %, so It may feel "unfair" not to have an operator for it:
Operators are somewhat more privileged than regular functions, so why should a less useful algorithm deserve what mod isn't granted?

But for me, "%" has no strong connection to remainder or modulo - which "mod" has.
As operators in Swift can't contain regular characters, methods on the various integer types are my preferred choice, and I don't care much what "%" does.

Operators save some keystrokes, but they are also less discoverable than methods.
The square root, for example, might even be used more often than mod, but it does not seem common to have an operator for it (and Swift even decided to make it a method, instead of the common sqrt(doubleValue).

It might well be possible that "%" does more harm than good, and I actually don't see a strong connection to "/" - but imho the evolution process is to tiring for breaking changes like redefining an operator, so I'd strongly prefer to keep this separated from the addition of needed functionality.

5 Likes

Sorry, to revive this thread, but it was just brought to my attention through the "even and odd integers" thread. :slight_smile:

It was stated that

This is false, at least to my knowledge. There is no "standard" behaviour for the modulo operator when it comes to negative numbers, as the table on the Wikipedia page illustrates. Everyone does it differently, and I suspect that most people don't have any strong expectations about what % should do with negative numbers anyway. (That said, I tend to agree that such a breaking change now is probably too dangerous.)

There is a paper whose abstract states that

To my knowledge, Swift doesn't use any of those two definitions.

Rust implemented special functions for euclidean division and remainder and it might be worthwhile to refer to the RFC for that change which gives some more rationale.

1 Like

According to this table, the overwhelming majority of programming languages, including Swift, uses "Result has same sign as Dividend" for the % operator. So, the statement is true.

That is true, but when talking about % specifically, the vast majority of languages that use it, have the same behaviour as Swift. Of the 43 languages in the tale that use % 30 set the sign of the remainder to be that of the dividend (i.e. the same as Swift). Of the thirteen that don't, two are obsolete versions of C and C++. The only significant languages in modern terms that buck the trend are Python and Ruby.

1 Like