2

While thinking about the way to implement Scheme R5RS, I have become puzzled about the following extract of R5RS (pages 22-23):

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

(lcm 32 -36) ==> 288
(lcm 32.0 -36) ==> 288.0 ; inexact

(denominator (/ 6 4)) ==> 2
(denominator (exact->inexact (/ 6 4))) ==> 2.0

Should we understand that, even if -4.0, 32.0 and (exact->inexact (/ 6 4)) are inexact, the implementation shall "remember" their exact equivalent (-4, 32 and 3/2) in order to proceed to integer division, prime factors decomposition, etc?

Otherwise, how could the implementation succeed in giving the above answers?

Thanks in advance for any light you could throw on this subject! :)

Nicolas

Nicolas_75
  • 95
  • 6

2 Answers2

1

There is no need for the implementation to remember the exact equivalent because as per R5RS it is OK to produce an inexact result given an operation involves an inexact operand. E.g:

> (+ -1.0 2)
=> 1.0

Internally, the interpreter can upgrade 2 to a float and call the addition operation for floats, there is no need to remember anything:

/* Assuming that the interpreter implements primitive operations in C. */
SchemeObject* addInts(SchemeObject* a, SchemeObject* b)
{
    if (a->type == FLOAT || b->type == FLOAT)
    {
        cast_int_value_to_float (a);
        cast_int_value_to_float (b);
        return addFloats (a, b);
    }
    return make_new_int_object (get_int_value (a) + get_int_value (b));
 }

In effect, the above addition in Scheme is treated by the interpreter as:

> (+ -1.0 2.0)
=> 1.0   
Vijay Mathew
  • 26,737
  • 4
  • 62
  • 93
  • Thanks for your answer. I fully agree with you. However, if you look again in the extracts of R5RS in bold, the interpreter seems to be able to perform "integer" operations on "float" inexact numbers, for instance (denominator (/ 1.0 3.0)). This is what puzzles me. – Nicolas_75 Feb 10 '11 at 12:34
  • @Nicolas_75: `(denominator (/ 1.0 3.0))` does not have to return 3.0. And on most implementations, it in fact doesn't. (The result I get on my computer, on Racket 5.0.2, is 18014398509481984.0. If that doesn't make sense to you, see my post.) – C. K. Young Feb 10 '11 at 14:02
1

It doesn't have to "remember" the original exactness of the arguments. It can temporarily (internally) convert the numbers to exact during the calculation, and tag the result as inexact if any argument is inexact.

Examples:

(denominator 1/10)  ; 10
(denominator 0.1)   ; 3.602879701896397e+16

(The latter result is implementation-dependent. The number I quoted is from Racket 5.0.2 running on amd64. You'll get different results from other implementations.)

For lurkers and archives: the unusual-looking result is because most implementations use IEEE 754 for inexact numbers, which (being a binary floating-point format) cannot represent 0.1 with full precision (only a decimal floating-point format can).

In fact, if you use (inexact->exact 0.1), you will not get 1/10, unless your implementation uses decimal floating-point.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435