Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Positive modulo #4

Closed
Mercerenies opened this issue Jun 14, 2023 · 8 comments
Closed

Positive modulo #4

Mercerenies opened this issue Jun 14, 2023 · 8 comments
Assignees
Labels
accepted 🚀 For accepted issues and pull requests feature ✨ For feature requests and implementations

Comments

@Mercerenies
Copy link

Here's a short function that I throw in every project I ever write, pretty much. The built-in Game Maker modulo operator % takes the sign of the dividend, matching the behavior of C. This can cause awkwardness when using % to perform wrapping behavior on a value that might be negative. This is a variant of the modulo function that matches the sign of the result to the sign of the divisor.

function modulo(a, b) {
  return (a % b + b) % b;
}
@Alphish
Copy link
Owner

Alphish commented Jun 14, 2023

Generally in favour of this one, and for the div counterpart as well (i.e. div that rounds towards -infinity, instead of towards 0).

Wondering about the name. modulo doesn't quite show how it's set apart from the mod keyword.

Reading about division, there's apparently the concept of Euclidean division which more or less matches :
https://en.wikipedia.org/wiki/Euclidean_division

So I consider function names like:

  • mod_euclidean and div_euclidean (kinda mouthful, and people might not quite know what Euclidean division is supposed to be)
  • eucmod and eucdiv (terser, but still relies on knowledge of Euclidean division)
  • math_mod and math_div (as in, the way mathematicians handle modular arithmetics)

We might need to accept the fact that there's no super-intuitive name that would make users go "oh, yes, it's the kind of mod/div that handles negative values properly!" and explain things in JSDoc instead. Still, other name suggestions welcome.

@JonathanHackerCG
Copy link

JonathanHackerCG commented Jun 14, 2023

I agree with this suggestion. As for name in my own code, I opted for the emod and ediv (even terser).

EDIT: My second suggestion was silly because I misread the post.

A third suggestion is maybe mod_signed and div_signed?

@gnysek
Copy link

gnysek commented Jun 15, 2023

emod and ediv looks like fast to use

@Alphish Alphish added the feature ✨ For feature requests and implementations label Jun 15, 2023
@Mercerenies
Copy link
Author

Actually I'm fond of eucmod and eucdiv now that you say that. It's short enough to still type easily, but long enough that anyone who knows what Euclidean division is will immediately recognize the prefix.

@Alphish
Copy link
Owner

Alphish commented Jun 16, 2023

Alright, then maybe let's do some voting on the naming? Since emod/ediv and eucmod/eucdiv seem to be most popular:

  • 🎉 for emod/ediv
  • 🚀 for eucmod/eucdiv
  • 😕 for something else altogether

People seem to agree that it would be a nice addition, and it seems naming is the only remaining thing to settle before proceeding with implementation.

@Alphish Alphish added the name voting ⚖️ The feature is pretty much accepted, we just need to decide on a name label Jun 18, 2023
@Alphish Alphish pinned this issue Jun 18, 2023
@Alphish Alphish added accepted 🚀 For accepted issues and pull requests and removed name voting ⚖️ The feature is pretty much accepted, we just need to decide on a name labels Jun 19, 2023
@Alphish
Copy link
Owner

Alphish commented Jun 19, 2023

Alright, those who voted agreed that eucmod/eucdiv works better, so that's what we're going with. If someone wants to implement it, they can go ahead.

When it comes to eucdiv, I think one of the simplest implementations would be:

function eucdiv(_a, _b) {
    return (a - eucmod(_a, _b)) div _b;
}

Or at least, it matches the definition of Euclidean division.

The tests should verify that:

  • eucmod always ends up between 0 and abs(b) (the divisor), regardless of dividend and divisor signs (except when divisor is 0, then it's borked)
  • the following is always true, regardless of dividend and divisor signs: a = eucdiv(a, b) * b + eucmod(a, b)

When it comes to the demonstration, I imagine some grid-snapped input would illustrate it pretty well.
So the user moves a mouse around, and there's a comparison between eucdiv and eucmod versus regular div and mod, calculated on (mouse_x - x) and (mouse_y - y), with x and y being coordinates of the grid object and the cell size being the divisor.

If someone does start the implementation work, please tell it here to avoid other people duplicating your work and allow better coordination in general.

@devKathy
Copy link
Contributor

devKathy commented Jun 22, 2023

I will go ahead and say that I am starting the implementation work. I will let folks know if any issues arise, or if I am unable to complete it due to schedule conflicts, etc.

I'm adjusting Merc's algorithm to be correct with Euclidian division mods in the case of negative divisors. It seems to me that what he has as of now is based on floor division.

function eucmod(a, b) {
   b = abs(b);
 return (a % b + b) % b;
} 

For more on Euclidian/floor division, see (Look especially at the C implementations!):
https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote.pdf

You can see people doing various implementations of floor division here:
https://stackoverflow.com/questions/10065080/mod-explanation?rq=3

Take care, folks! :)

@Alphish
Copy link
Owner

Alphish commented Jun 29, 2023

The related PR has been merged, and thus I'm closing this issue.

@Alphish Alphish unpinned this issue Jun 29, 2023
@Alphish Alphish mentioned this issue Jul 2, 2023
@Alphish Alphish added this to the 23.4.0 Release milestone Aug 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted 🚀 For accepted issues and pull requests feature ✨ For feature requests and implementations
Projects
None yet
Development

No branches or pull requests

5 participants