6

In C, we have the % operator which finds remainder of division. But it's only applicable to integral types. For floating-point types we have to use fmod function from math.h. This makes an impression that floating-point types are second-class citizens in C.

So, why was the modulo operation demoted to a library function for floating-point types compared to core language operator for integral types?

11

Actually, the answer appears to have been documented in C99 Rationale, see the notes on §7.12.10.1 The fmod functions:

The C89 Committee considered a proposal to use the remainder operator % for this function; but it was rejected because the operators in general correspond to hardware facilities, and fmod is not supported in hardware on most machines.

  • ...while it is supported in hardware in most FPUs (68881 had it, 8087 had it) – tofro Apr 28 at 6:58
  • @tofro 8087 only had a partial remainder instruction FPREM. One had to repeat this instruction multiple times in general to compute the full remainder. – Ruslan Apr 28 at 7:05
  • 1
    This should be the 'accepted' answer. The Rationale document exists precisely to answer questions about 'Why...'. – another-dave Apr 28 at 11:59
5

Because mathematically speaking, the modulo operation or division with remainder makes only sense on integers (or more generally on Euclidean Rings). This operation doesn't make sense on any (mathematical) field like the real numbers (or there computer approximations float and double) at all, because in such a structure a division can always be done without any remainder.

As a consequence, on an Euclidean Ring like the integers Z you get the derived quotient rings Z_n which consist (formally) of elements of Z with the same reminder module n, and these have applications in coding theory etc. There is nothing equivalent for the real numbers R.

So fmod is an afterthought which takes one particular definition of division with reminder and implements it in an ad-hoc way on floats and doubles by truncating the result of the division. The only use for such a function is if you have accidentally stored integers in float or double variables, and if you want to save yourself the trouble of conversion to integer variables.

So from this point of view, it's completely logically that % is an integral part of the language, while fmod is just a library function.

  • In what sense is the field of real numbers that are congruent mod x any less reasonably defined than the ring of integers that are congruent mod m? Such fields arise quite naturally if one e.g. defines p and q as being equivalent any time tan(p) and tan(q) have the same value (which would occur if p and q differ by some multiple of pi). The biggest difference I see between floating-point modulus and integer modulus is that the former would often be most usefully defined as being the smallest-magnitude residual, rather than the smallest non-negative one. – supercat Apr 29 at 21:49
  • @supercat For starters, it's not a field. All fields have the zero product property (proof: multiply by the resp. inverse from each side), but for any real x (which is "congruent" to zero "mod x"), you can find many factorizations of x, and hence many factorizations of zero. What you describe is a quotient ring, but not one with really interesting properties, so it's doubtful if it's important enough to deserve a function like fmod. It doesn't hurt to have it, but it shouldn't be part of the core language. – dirkt Apr 30 at 6:02
  • There is nothing about integers that makes the concept of "equivalence mod x" more applicable to them than to any other group. Two items p and q are equivalent mod x if they are equivalent in the original group, if p+x is equivalent (mod x) to q, or if q+x is equivalent (mod x) to p. What makes you view the determination of an integer's modulus any more special than that other rings or quasi-rings such as floating-point numbers? – supercat Apr 30 at 17:55
  • @supercat That's true, except that you don't call it equivalence, but congruence (equivalence + compatibility with operations). What you do not* call it is "modulo", "mod", "division with remainder", etc. That's reserved for the integers (or, more generally, Euclidean Domains). No mathematician would call fmod a "modulo operation". So for the integers mod has a very specific canonical meaning. fmod doesn't, and there are many other ways to build congruences on the reals seen as a ring (and more useful ones), so it's a bit strange fmod should get prominence. – dirkt May 1 at 12:08
  • Sorry it's been decades since I've taken abstract algebra, so my terminology isn't perfect. In any case, if for any ordered group G one can define operators scMul(Z,G)->G, scDiv(G,G)->Z, and scMod(G,G)->G, which will behave for that group in a fashion analogous to how multiplication, floored division, and modulus operators work on integers; further, the behavior of those operators on integer-valued real numbers would match their behavior with integers. As implemented, IEEE decided not to make fmod() compute the floating-point modulus, but that doesn't mean the latter concept... – supercat May 1 at 14:47
1

I see two reasons for offering a % operator but an fmod() function. The first is complexity. On many CPUs integer division is essentially just a single opcode or an inline simple chain (such as would be needed for an 8-bit CPU). Thus, representing direct CPU capability with an opcode makes intrinsic sense. There is a second form of complexity as well. The compilation of % expects integer data, while fmod() expects double float data. Having a function call that would change its operation depending on type would add extra complexity that programmers are perfectly able to handle themselves.

The second reason is utility. % is is a useful and common basic arithmetic operator. Making it native to the language just as is "+" makes a lot of sense. In my experience fmod() is much less so. I have programmed many varieties of software, but I cannot remember ever needing the fmod() function. Others have a different experience, I'm sure, but overall I think the integer operation is needed far more often. Stick the lesser used thing in a math library, and don't bloat the base language with them.

  • The second form of complexity applies to any of +-*/, but these have still been implemented for all the types. – Ruslan Apr 28 at 6:40
  • 1
    re: "a function call that would change its operation depending on type ..". Fair enough, but we could trivially avoid that by implementing int imod(int, int). The question is then why we have % and fmod, rather than imod and fmod. – another-dave Apr 28 at 11:53
  • @another-dave If one thinks of % not as the "modulus" operator but the "remainder" operator, then it perfectly matches with / . I.e. / returns the integer quotient of division whereas % returns the remainder (fractional) part. I.e. 23 divided by 7 is three and two sevenths. It is convenient that with integers, the remainder is also the modulus, but remainder is what we learn first (at least when I went to school). – RichF Apr 29 at 11:30
  • @Ruslan it definitely is convenient that the compiler has overhead to interpret the basic arithmetic operations in accordance with the types they operate on. I think, though, that % only has natural meaning in regards to integer division. Did anyone ever use a real number modulus prior to it being in computer languages? – RichF Apr 29 at 11:48
  • 2
    I think range reduction like fmod(x, 2*M_PI) was useful before computers to bring x to the range where some polynomial approximation of e.g. trigonometric functions could be applied. And this reduction is still useful today, even when we simply want to remove extraneous revolutions from an angle. – Ruslan Apr 29 at 13:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.