• Subject: Re: Implementation of %
• From: Rici Lake <lua@...>
• Date: Wed, 18 May 2005 12:57:45 -0500

```
On 18-May-05, at 11:46 AM, Aaron Brown wrote:

```
```Rici wrote:

```
```Is there a good reason why % is implemented in terms of
floor rather than fmod? The following seems curiously
inconsistent to me:
```
```
I made a chart comparing math.mod(-6 through 6, -3 through
3) to (-6 through 6) % (-3 through 3).  When the two give
different results, the math.mod result is to the left of the
colon.
```
```
It's pretty easy to summarize: math.mod (like fmod) returns a value
with the sign of the numerator (first argument). % returns a value
with the sign of the modulus (second argument).

Given the ieee-754 function copysign(x, y) (which returns x with
the sign of y), you can convert either behaviour into the other
one:

other_mod(n, m) == mod(copysign(n, m), copysign(m, n))

I'm not a big fan of the C behaviour, either (which afaik applies
to both integer % and fmod), although it is perfectly reasonable
if you consider % to be a remainder operator rather than a modulus
operator, which is its official name in the C standard (not that
that matters, really since this is Lua, not C). For what it's worth,
the C standard requires fmod to have the behaviour indicated above;
the original C spec did not state the result of % with negative
arguments (which has got to be the worst) and I believe that C99
requires % to behave like fmod (which is to say, actually be a
remainder).

The latest draft of IEEE-754 that I've seen also defines a
remainder operation rather than a modulus operator, although
its behaviour is even quirkier; the division is rounded to the
nearest integer, so that remainder(5,7) is -2.

So, as they say, the nice thing about standards is that there
are so many to choose from.

However, it seems to me that (x - (floor(x/y) * y) may do a
lot more work than I really wanted it to :) and might introduce
rounding errors, as well:

> -- A large random number
> a = 5465193658635553
> =math.mod(a, 2.1)
1.3688539491866
> =a%2.1
1

> -- % yields a negative result with positive arguments
> =math.mod(6.3, 2.1)
2.1
> =6.3%2.1
-8.8817841970013e-16

> -- Note the inexactness of 6.3 and 2.1:
> =string.format("%21.17f %21.17f", 6.3, 2.1)
6.29999999999999982   2.10000000000000009

```