3

I'm reading R5RS spec and it shows this:

(modulo 13 4)                   ===>  1
(remainder 13 4)                ===>  1

(modulo -13 4)                  ===>  3
(remainder -13 4)               ===>  -1

(modulo 13 -4)                  ===>  -3
(remainder 13 -4)               ===>  1

(modulo -13 -4)                 ===>  -1
(remainder -13 -4)              ===>  -1

(remainder -13 -4.0)            ===>  -1.0  ; inexact

Is this correct? I thought that modulo and remainder differ only in minus sign. And here it shows that (modulo -13 4) should return 3, in JavaScript it returns 1.

What are proper algorithms to calculate modulo and remainder? I need this for my Scheme in JavaScript implementation.

I've found this code at quora.

function modulo(num1, num2) {
  if (num2 === 0 || isNaN(num1) || isNaN(num2)) {
    return NaN;
  }

  var isPositive = num1 >= 0;

  num1 = Math.abs(num1);
  num2 = Math.abs(num2);

  while (num1 >= num2) {
    num1 = num1 - num2;
  }

  return isPositive ? num1 : -num1;
}

but it don't work like in R5RS spec, it returns -1 for modulo(-13, 4). Also I thought that JavaScript's % is the same as remainder. How to implement both functions in JavaScript or in Scheme?

My exact question is: how the algorithm for both functions should look like or how JavaScript code to calculate them both should look like?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
jcubic
  • 61,973
  • 54
  • 229
  • 402
  • Is this correct? Yes. Different languages define the modulo and remainder operations differently, as explained in [here](http://en.wikipedia.org/wiki/Modulo_operator). – Óscar López Mar 25 '20 at 18:28
  • @ÓscarLópez do you know if that 3 is correct or is just typo in the spec? – jcubic Mar 26 '20 at 08:01
  • It's correct, just try it in any implementation compliant with the spec. Javascript simply does things differently, you'll have to adapt it to match Scheme's spec. Also take a look at this [post](https://stackoverflow.com/questions/3883004/the-modulo-operation-on-negative-numbers-in-python), Python is similar to Scheme in this regard and it'll help you understand how and why the modulo is calculated in a different way. – Óscar López Mar 26 '20 at 08:04
  • @ÓscarLópez The problem is I don't see how this should be implemented, I see only examples in the spec no explanation whatsoever. [R5RS ch 6.2.5](https://schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_sec_6.2.5) – jcubic Mar 26 '20 at 08:09
  • The Python post I linked above will give you some hints, but I guess that you'll have to figure it out yourself. Or, you could look at the source code of one of the many Scheme interpreters already available and see how they did it. – Óscar López Mar 26 '20 at 08:21
  • @ÓscarLópez I was looking at guile but it didn't help. I've tried to check biwacheme but it have remainer and modulo in TODO list. Thanks for your help anyway. – jcubic Mar 26 '20 at 08:43
  • 1
    your own link provides an explanation, *above* the examples. I've edited your question to add the more precise link, check it out. :) – Will Ness Mar 27 '20 at 08:29
  • @WillNess I don't understand this wording, English is not my native language, do you know how to write this as JavaScript code that return same results as examples? – jcubic Mar 27 '20 at 13:55
  • I don't know Java from Javascript. :) Would C code help? Would Scheme? – Will Ness Mar 27 '20 at 14:39
  • @WillNess yes, but it need to have standard operations, I'm not sure how % works, each language have have different implementation but `+-/*` is the same. same as floor and ceil, if you know how using those can create implementation in Java or C I'm fine. – jcubic Mar 27 '20 at 15:36

4 Answers4

2

This is Chibi's implementation:

The author is one of the authors of R7RS.

ceving
  • 21,900
  • 13
  • 104
  • 178
  • the scheme code in Chibi will be helpful I was searching for the source code, I've checked Kawa and Guile and there most of the code was in host language and hard to read. – jcubic Mar 27 '20 at 19:11
1

Here is how I implemented the functions in Urlang:

    (define/export/arity (quotient n m) 
      (Math.floor (/ n m)))

    (define/export/arity (remainder n m)
      (% n m))

    (define/export/arity (quotient/remainder n m)
      (values (quotient n m) (remainder n m)))

    (define/export/arity (modulo n m)
      (var [who (λ() (string->symbol "modulo"))])
      (unless (and (number? n) (not (infinite? n))) 
         (raise-argument-error (who) "integer?" 1 n m))
      (unless (and (number? m) (not (infinite? m))) 
         (raise-argument-error (who) "integer?" 2 n m))
      (% (+ (% n m) m) m))

Here Math.floor, % and + are the standard JavaScript functions/operators.

For the curious, here is the JavaScript produced:

function remainder(n,m){if(!((arguments.length===2)===false))VOID;else (raise_arity_error_m((string__gsymbol("remainder")),2,arguments));;return (n%m);};
function quotient_qremainder(n,m){if(!((arguments.length===2)===false))VOID;else (raise_arity_error_m((string__gsymbol("quotient/remainder")),2,arguments));;return (values((quotient(n,m)),(remainder(n,m))));};
function modulo(n,m){if(!((arguments.length===2)===false))VOID;else (raise_arity_error_m((string__gsymbol("modulo")),2,arguments));;var who=(function(){return (string__gsymbol("modulo"));});(((!((number_p(n))&&(!(infinite_p(n)))))===false)?undefined:((function(){return (raise_argument_error((who()),"integer?",1,n,m));})()));(((!((number_p(m))&&(!(infinite_p(m)))))===false)?undefined:((function(){return (raise_argument_error((who()),"integer?",2,n,m));})()));return (((n%m)+m)%m);};

UPDATE

Here is a little context. The code implements remainder and modulo for a Scheme runtime. The runtime is implemented in Urlang (which is JavaScript with s-expression-syntax).

From the output JavaScript, you can see that:

  • Scheme remainder is implemented as n%m.
  • Scheme modulo is implemented as ((n%m)+m)%m

This assumes that Scheme numbers are represented as JavaScript numbers (i.e. no bignums).

soegaard
  • 30,661
  • 4
  • 57
  • 106
  • Urlang is not heplful, whatever language it is. The code have `%`, in every lanaguage this operator works differently, in JavaScript % is not reminder, so I can't use this implementation. I'm fine in any language that don't use %, there is JS code but it's obfuscated. – jcubic Mar 27 '20 at 19:08
  • @jcubic I have added a little context. – soegaard Mar 27 '20 at 19:12
  • I think that `quotient` is not correct, it need to be `ceil` if n is negative. – jcubic Mar 27 '20 at 19:25
  • Your `reminder` is much shorter then implementation in scheme, I thought that JS `%` is not scheme `reminder`, but it seems it is and only `modulo` is different. Thanks. – jcubic Mar 27 '20 at 19:28
  • It confused me at first - I am used to C where % is modulo. – soegaard Mar 27 '20 at 20:13
  • I knew that % is not modulo in JavaScript I've only tried to use it as scheme reminder, but I thought that it was not scheme reminder after all, I was testing against this `list` in my answer, I didn't check carefuly that the reminder with % work in fact corectly. – jcubic Mar 28 '20 at 09:57
1

If someone is interested I've asked the same question on Reddit (with link to this question) and got the answer with exact scheme code:

https://www.reddit.com/r/scheme/comments/fpt1b8/help_with_modulo_and_reminder_in_r5rs/

(define (modulo a b)
  (- a (* b (floor (/ a b)))))

(define (remainder a b)
  (- a (* b (truncate (/ a b)))))

;; as @soegaard show reminder is just JavaScript % so this can be
;; if % is proper function
(define (remainder a b)
  (% a b))

it works the same with examples from R5RS:

(list
  (= (modulo 13 4) 1)
  (= (remainder 13 4) 1)      ;; ===>  1

  (= (modulo -13 4) 3)        ;; ===>  3
  (= (remainder -13 4) -1)    ;; ===>  -1

  (= (modulo 13 -4) -3)       ;; ===>  -3
  (= (remainder 13 -4) 1)     ;; ===>  1

  (= (modulo -13 -4) -1)      ;; ===>  -1
  (= (remainder -13 -4) -1)   ;; ===>  -1

  (= (remainder -13 -4.0) -1.0)) ;; ===>  -1.0  ; inexact

floor is Math.floor and truncate is:

var truncate = (function() {
    if (Math.trunc) {
        return Math.trunc;
    } else {
        return function(x) {
            if (x === 0) {
                return 0;
            } else if (x < 0) {
                return Math.ceil(x);
            } else {
                return Math.floor(x);
            }
        };
    }
})();
jcubic
  • 61,973
  • 54
  • 229
  • 402
1

You might be interested in SRFI 141: Integer division, by Taylor Campbell. It is a proposed extension to Scheme that "...provides a fairly complete set of integral division and remainder operators."

  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/33013340) – L8R Oct 27 '22 at 17:34
  • Sorry, I'm a Stack Overflow novice. I intended this to be supplemental information rather than an answer. Would it have been better if I had made it a comment on the original question rather than an answer? – Arthur A. Gleckler Oct 28 '22 at 18:30