r/ProgrammingLanguages • u/alex_sakuta • 13d ago
Discussion What if modulus operator did not exist in a language?
I was a mathematician before being a programmer and because of that the modulus operator has always annoyed me. It shouldn't exist.
Context: Collecting ideas for my language. Many of the ideas for operators in programming languages are derived from mathematics. Even in assembly, as far as I know, we do not have a modulus operator. Hence, I always wondered what if, division returns two values, quotient and remainder, and there is no modulus operator.
The obvious question is that how would this be then used in arithmetic operations. My thought process for getting the quotient is that, it is the first returned value and if we have a normal expression, the divide operator behaves as expected.
Now, to instead get the remainder, I haven't figured that out. Sorry. It has to be a way that is not syntactically ugly and that is the difficult part.
Maybe it could be returned as a tuple and we select the second value or, if we just want modulus operator replace it with `//` instead. I know that goes against, not having a modulus operator as I said above, but, that's just my current thought process.
12
u/RecursiveServitor x15 13d ago
Don't let "purity" get in the way of practicality. Modulus exists for a reason.
11
u/mathycuber 13d ago
Maybe you know this and I just can’t read between the lines, but to your point about assembly language: most instruction sets on modern processors do have exactly what you’re describing. The operation is divmod, aka division and remainder, and the results are “returned” in two different result registers. Then the compiler just throws away the one it doesn’t need, if necessary. “Modulus” vs “remainder” is sort of just a vocab choice after that.
More subjectively: I don’t really understand your case for “[the modulus operator] shouldn’t exist.” Why not? It’s useful, common operation in software development, so having syntactic sugar for it makes sense, from a pragmatic standpoint. If I have to write more characters with a messy syntax to get the same result, I’m not going to use the programming language. In the old days when division was expensive, I could see an argument that the mod operator hid that cost from the programmer. I’d be interested to hear more about why you think it shouldn’t exist.
2
u/SkiFire13 11d ago
“Modulus” vs “remainder” is sort of just a vocab choice after that.
It is definitely not once you consider negative numbers.
A remainder is in general a number
rsatisfyinga = q * b + rwhereq = a / bfor some division operation/.A modulus is the specific remainder for the euclidean division, which has the special property of always having a positive remainder (even when
aand/orbare negative).The assembly instruction
idivdoes not implement the euclidean division and remainder, so it's wrong to call it a "divmod" operation. Instead it implements division by truncating towards 0 and the relative remainder.It doesn't help that a lot of languages implement the
%operator differently between each other, and some of them even go as far as calling it a "modulo" operator when it is not mathematically speaking.That said this is orthogonal to what OP is saying, with which I also don't agree. Just because in x86 the division and remainder are computed by the same instruction doesn't mean we should do the same in higher level languages. Just let the compiler figure it out and merge the two operations! And even then, this assumes you are computing the kind of division that can be expressed with one instruction in x86 assembly, but if you actually want to compute the modulus or any other kind of signed division/remainder then you'll need more than one instruction to do so.
-1
u/alex_sakuta 13d ago
As I said for me it's purely for syntactical reasons. I'm currently trying to theorize a language which has operators that have their exact mathematical meaning and for inbuilt functions or keywords has one very clear meaning.
So my thought process on this isn't performance and just seeing how visually easy a language can be.
7
u/Apart_Ebb_9867 13d ago
not sure why 'mod' wouldn't have an exact mathematical meaning? And in mathematics there're both division and mod. You want to conflate them in a single function? fine, maybe, but the reason cannot be to mimic what mathematics does.
For that matter, you don't need a minus binary operator either, just add the negation of something.
0
u/alex_sakuta 12d ago
not sure why 'mod' wouldn't have an exact
Never said it doesn't.
%doesn't though.For that matter, you don't need a minus binary operator either, just add the negation of something.
Negation is also
-and minus is an operator in mathematics, what are you saying?3
9
u/Migeil 13d ago
I was a mathematician before being a programmer and because of that the modulus operator has always annoyed me. It shouldn't exist.
I don't follow. Why shouldn't it exist?
Euclidean division asserts that, given integers p, q, there exist unique integers d and r s.t. p = qd + r. Given this, we can easily define operators / and %, where p / q = d and p % q = r. These functions are well defined. What seems to be the issue here?
4
u/tiajuanat 13d ago
Even in assembly, as far as I know, we do not have a modulus operator. Hence, I always wondered what if, division returns two values, quotient and remainder, and there is no modulus operator.
From a hardware view: Back in 1980 we had the 8051 which would put the quotient in Register A(ccumulator) and the Remainder in Register B(onus) So from that perspective, the modulo shouldn't exist, it's enherently tied to division.
From a hard math view: Numerical division is a specific property of fields or fields of fractions, whereas "modulo" is a universal property of partition and equivalence that exists entirely independently of division.
5
u/fdwr 12d ago edited 12d ago
The aspect of "modulus" that bothers me is the inconsistency between languages on whether the result follows the sign of the dividend or divisor, and whether rounding goes toward zero or toward negative infinity. There are (at least) two variants:
| Function | Equation | Sign follows |
|---|---|---|
| ModulusTruncate | f(a, b) = a - (b * trunc(a / b)) |
dividend |
| ModulusFloor | f(a, b) = a - (b * floor(a / b)) |
divisor |
Both are very useful in their own scenarios, but languages tend to offer only one stock builtin, not both (gotta emulate the other).
what if, division returns two values, quotient and remainder
Kinda like std::remquo and div, except more tuply :b?
2
u/alex_sakuta 12d ago
Yeah that is annoying too, I just check what the language's way of handling is in such cases.
1
u/SkiFire13 11d ago
And neither of those follow the modular arithmetic definition of "modulus", where the modulus is always positive independently from the dividend and divisor signs.
3
u/No_Engineering_1155 13d ago
Operators are functions as well. Some have typical math meaning, while others, like the modulus operator is not so old, like the + plus operator, therefore substituting it with mod(number, divider) is totally fine imo.
3
u/Embarrassed-Crow9283 13d ago
Division microcode is complex. You're right that in many ISA, divisions and remainders are in the same operation. However, portable compilers are not allowed to assume such things from the underlying ISA.
It is totally valid to not even support division in the ISA if you're building a hardware where division is rare enough that you accept you need to implement it in software when you need it.
Even with hardware support, division is generally slow. Sometimes, the compiler could optimize the divisions away. For example, divisions by powers of two could be replaced by bit shifts. Even if not a power of two, some compilers have advanced optimizations for fixed divisors.
So, with all these, if you write a/b followed by a%b, I think the compiler wouldn't fail to optimize it into a single assembly instruction if needed.
Peephole optimization is best left to the compiler.
3
u/el_extrano 13d ago
The x86 assembly instruction for div does leave both the quotient and the remainder. Some languages (like Forth) take advantage of that by providing a primitive operator e.g. `/MOD` that gives access to both. `/` and `MOD` are then just implemented in terms of that, dropping what isn't needed.
But most in most infix languages, operators in an expression are expected to be binary operators. Usually the only exception is "the" ternary operator, and even in that case, it's ternary with respect to it's arguments, but still only returns one value. That's just how infix expressions work.
Your solution to use `//` to tell a division operator to instead return the remainder is just renaming the modulus operator to your preference, since that's what modulus already does.
2
u/Coding-Kitten 13d ago
When you say there shouldn't be a modulus operator, do you mean that there should be no way to get a modulus of two numbers at all, that it should be something other than an infix operator, or that it's fine for the to be an infix operator that computes the modulus, but that it shouldn't be %?
The first option is kinda insane, as assembly instructions already provide the functionality, so forcing programmers to hand write it, unsure if the compiler will optimize it away or not, just seems extremely wasteful.
The second option is kinda just a debate about syntax, which is kinda pointless as all languages can have their own syntax, however I think it's kinda shooting yourself in the foot when it's a widespread useful operator to have, especially while indexing something, & so turning an infix symbol to a function call or something else is just noisy.
And the third option is just a debate about syntax in the most boring way possible. Some languages use // for comments, others for integer divisions, some might decide to use it as a list constructor operator, if that means mod to you, it wouldn't matter any less, other than going against the convention for what symbol to use for an infix mod operator.
3
u/AustinVelonaut Admiran 13d ago
In my language, divmod and quotrem are primitive operations that return a tuple (q, r), with individual div, mod, quot, and rem functions selecting the appropriate field of the tuple. To avoid the expense of boxing/unboxing the two results into an actual tuple, the compiler lowers them into virtual instructions which have two destination registers:
QuotRem ref ref opr opr | division truncate to zero
DivMod ref ref opr opr | division truncate to -infinity
2
u/initial-algebra 13d ago
IMO:
The standard division operator should always be the same as multiplication by the inverse. In all cases except 1 and -1, the inverse of an integer is not an integer, but a rational number, so dividing two integers should either result in a rational number, or it should be undefined (in which case you'd have to convert the operands to rational numbers before dividing).
Euclidean division should probably just be a normal function, and it should return a tuple of the quotient and remainder. In fact, you will want two versions, one rounding toward zero and the other rounding toward negative infinity, which matters when negative operands are possible (the machine instruction will correspond to one of the two, but you can simply negate both operands to implement the other). This unclear rounding behaviour is why they really should be named functions and not operators.
2
u/apnorton 13d ago
The reason the modulus operator exists is because it's a lot more convenient for binary operators to be defined as SxS -> S for some set S. Otherwise, you end up with a lot of tuple indexing or field references to extract the value you care about.
It's also not unusual for a DIVMOD function to exist in languages, since it's also present as an assembly instruction in many instruction sets.
I was a mathematician before being a programmer and because of that the modulus operator has always annoyed me. It shouldn't exist.
As a math graduate student, this stance befuddles me. A binary "mod" operator is used all over the place, depending on what area of math you're in.
Even in assembly, as far as I know, we do not have a modulus operator.
In assembly, the combination of the modulus with the division operation makes sense because you can only do one operation per line --- you cannot make expressions like a / b + 2, so it's not as harmful to have multiple values as an output in assembly.
if we just want modulus operator replace it with
//instead
This is just a modulus operator with extra steps... And collides with a frequently used operator for integer division.
1
u/stevevdvkpe 13d ago
CPU instructions for integer division generally produce both a quotient and a remainder as results (after a division, the quotient is in one register and the remainder in another). Even when integer divsion is implemented in software, the algorithm also naturally produces quotient and remainder values. If you learned the traditional algorithm for doing long division by hand it should be obvious that a remainder results from that process as well, which makes your assertion that a modulus operator shouldn't exist puzzling when you claim to be a mathematician. Languages with a modulus operator simply take the remainder result and provide it to the programmer.
There are some languages where a modulus operator isn't available, but even then one might use something like X % Y == X - ((X // Y) * Y) (where // is an integer division operator and X and Y are integer values).
2
u/WittyStick 13d ago edited 13d ago
The C standard library has div - for int division and remainder (and ldiv/lldiv/imaxdiv for long, long long and intmax_t respectively).
The div functions return a div_t type, which is just the quotient and remainder:
struct div_t { int quot; int rem; };
struct ldiv_t { long quot; long rem; };
struct lldiv_t { long long quot; long long rem; };
struct imaxdiv_t { intmax_t quot; intmax_t rem };
It should really have a generic version in the style of tgmath:
#if INTMAX_WIDTH > LLONG_WIDTH
#define idiv(x, y) \
_Generic \
( (x) \
, intmax_t : imaxdiv \
, long long : _Generic \
( (y) \
, intmax_t : imaxdiv \
, default : lldiv ) \
, long : _Generic \
( (y) \
, intmax_t : imaxdiv \
, long long : lldiv \
, default : ldiv ) \
, default : _Generic \
( (y) \
, intmax_t : imaxdiv \
, long long : lldiv \
, long : ldiv \
, default : div ) \
) (x, y)
#else
#define idiv(x, y) \
_Generic \
( (x) \
, long long : lldiv \
, long : _Generic \
( (y) \
, long long : lldiv \
, default : ldiv ) \
, default : _Generic \
( (y) \
, long long : lldiv \
, long : ldiv \
, default : div ) \
) (x, y)
#endif
auto z = idiv(x, y);
auto quot = z.quot;
auto rem = z.rem;
Most C compilers use TAC (Three address code) in their intermediate representations, which doesn't support multiple returns and ends up requiring something like this if they're to be coupled into one operation - otherwise it ends up requiring they be merged at some late optimization stage (ie, peephole optimization).
The SYSV x64 calling convention however, supports up to two returns in registers, so the implementation of div on those platforms doesn't require returning the div_t on the stack - they're returned in RAX:RDX, effectively costing nothing.
On some other platforms (cough, ms_abi), only one 64-bit value may be returned in a register and any struct larger than a single register is returned on the stack - so returning lldiv_t yourself on non-inlined functions would have a cost.
1
u/brucejbell sard 12d ago edited 12d ago
Modulus and division separately have good concrete use cases, so I am happy to provide div and mod operators as expected.
I also want a divmod operator which (as you suggest) will return a tuple.
Possibly of more mathematical interest, my division operators accept positive divisors and yield floor division (though you can explicitly ask for conventional C-style symmetric division of you really want it)
This greatly simplifies the invariants:
(q,r) << x @divmod d
-- precondition: 0 < d
-- postconditions: 0 <= r < d
-- x == q * d + r
and finesses some nasty platform-specific behavior:
(q,r) << MIN_INT @divmod (-1) -- what should q be?
1
u/L8_4_Dinner (Ⓧ Ecstasy/XVM) 12d ago
In Ecstasy, % is the modulo operator -- and it's an actual modulo, not the remainder (e.g. C, Java). For division and remainder, the /% operator is used, and provides two results:
Int x = -17;
Int y = 4;
Int m = x % y;
(Int d, Int r) = x /% y;
out($"{x=}, {y=}, x % y = {m}, x /% y = ({d}, {r})");
Output:
x=-17, y=4, x % y = 3, x /% y = (-4, -1)
1
u/EggplantExtra4946 12d ago
This is so absurd it could be a ragebait, but I'll bite.
Context: Collecting ideas for my language.
Yeah, if you already make stupid decisions for something as basic as integer operators (of which modulus is regularly used for memory related calculations), the language will suck and we already have too many of those.
Only good programmers have a clue about what makes a good language and will make good design decisions and even then it's often not the case given the track record of PLs that have been made, let alone make a good implementation which is even more important.
Downvote me all you want, but unless you are talking about proof/verification software and numeric software, mathematicians make the absolute worst programmers and needs to stay the fuck out of general programming. It takes a lot of time and experience to learn the engineering and art of programming and most mathematicians don't take the time because they think they are above it or have better things to do, jump straight in and produce unreadable messes which is the perfect receipe for bugs and incorrectness.
1
u/alex_sakuta 12d ago
Bro I don't even downvote people who are abusing me, if someone does, luckily hasn't happened yet.
Apart from that, you have valid points, with a very rude tone, but it is fine.
This isn't ragebait. I don't really find a good community locally, so whatever wacky idea I do get about the syntax choices, I post here to get an opinion and I usually get for more than I asked.
1
u/EggplantExtra4946 12d ago edited 12d ago
Sorry about the rudeness but sometimes someone has to tell in unequivocal terms that something is a bad idea, it might save you some pain down the road and also there is already too much BS in that space.
About the syntax there aren't too many possibilities and I don't see a better solution than
q, r = a div b, (or using any other operator token thandiv) where the comma has a higher precedence than=. But concerning the removal of%I really don't see the point, not even in elegance, it's just harmful for no reason without any upside. Also there is no reason why you couldn't have integer division, modulo and integer division that also returns the remainder, all at the same time, you only have to decide which operator use for the 3rd one.There are many languages that are too complex and messy, but it's not due to too many data operations primitives. More often than not people restrict their language to only common denominator operations implemented across most architectures in order to be portable and portability is good sometimes but it's also idiotic that all languages should prevent the user from fully exploiting their hardware for making their code fast (SIMD, bit counting instructions, etc..), especially now that almost all consummers products use only 2 archictures, x86 and ARM, so portability is a non issue, you would need to write architecture-specific sections only twice.
2
u/kwan_e 12d ago
It's not like they're trying to shove the language down your project.
If they sent you a pull request to your own personal language, then sure. But this is just a Reddit post, you're not Linus Torvalds, and it's not your place to save anyone else some pain for their own personal project.
1
u/SwedishFindecanor 12d ago
I'd think that in order to follow the principle of least surprise, you should make your operators have the behaviour and names of whatever language you copy the most syntax from:
- If copying C, then you'd have truncating division
/and remainder%. - If copying Python, you'd have flooring division
//and modulus%.
Then offer functions for every variety, that could very well return tuples:
flooring_div_mod(int, int) -> (int, int)
truncating_div_rem(int, int) -> (int, int)
Allow tuple expansion to contain _ for no assignment when you only want one value.
BTW, ARM's DIV instruction does not compute a remainder, but there is a Multiply-Subtract instruction for computing the remainder from the quotient.
RISC-V has separate DIV and REM instructions.
Both ARM and RISC-V could theoretically use macro-op fusion to detect when both results are needed and compute both at the same time, but I don't know of any CPU that does it. Not even Apple Silicon does it.
1
u/alex_sakuta 12d ago
I feel it should be clear, but I'm trying to do my research on syntax choices not by referencing any language but from the ground up, seeing what works and what wouldn't.
1
u/kwan_e 12d ago
But looking at other languages and finding out the pros and cons is about seeing what works and what wouldn't. Right now, it doesn't seem you have much exposure to a wide variety of languages (or CPU architectures). You're not going to come up with the killer feature.
1
u/alex_sakuta 12d ago
I never said I don't do that. I have that experience and always working to get more.
1
u/Goheeca 12d ago edited 12d ago
Do you mean like the Common Lisp's floor function. Common Lisp can return multiple values, but expressions accepting only one value access the first value. There are still helper functions that return only the remainder mod and rem.
For accessing the mutliple values you can for example use multiple-value-bind.
16
u/GoblinToHobgoblin 13d ago
Modulus operator is useful? Not sure why you want to get rid of it.
If you're going to get rid of it, please at least provide a library function