Hi,

Tomas Volf <[email protected]> writes:

>> I lack the background information about numbers to judge what’s correct
>> here. Can you judge that?
>
> Returning NaN is consistent with Chez, but I am not qualified to say
> whether it is correct.  I just did basically white-box testing of both
> yours and the built-in version and took note of the differences. ^_^
>
>>
>> It seems like we’ll need a full set of tests for min and max before we
>> can replace them.

I just realized that we actually have quite a few tests.

The following is a slower version, but it passes all tests in our
numbers.test. It looks *much* slower, but in my test with one million
numbers, it’s factor 2 slower than min in C -- on the slow path. The
syntax-case part is faster than C.

The tests only check with small numbers of arguments, so only the
syntax-case is actually tested, the rest copied over in the hope to make
no mistake:


(import (ice-9 match))
(define-syntax-rule (define-minmax-expansion f %f compare)
  (begin
    (define %f
      (case-lambda
        (() (error 'wrong-num-args))
        ((x) x)
        ((x y) (cond ((compare x y) (if (inexact? y) (exact->inexact x) x))
                     ((compare y x) (if (inexact? x) (exact->inexact y) y))
                     ((eqv? x y) x) ;; not for -0. 0.
                     ((= x y) (if (zero? x) 0. x)) ;; -0. and 0.
                     (else (nan))))
        ((x . rest)
         (let lp ((m x)
                  (rest rest)
                  (inex? (inexact? x)))
           (match rest
             (() (if (real? m)
                     (if inex? (exact->inexact m) m)
                     (error "Wrong type argument" 'wrong-type-arg)))
             ((x . rest)
              (cond
               ((compare x m) (lp x rest (or inex? (inexact? x))))
               ((compare m x) (lp m rest (or inex? (inexact? x))))
               ((eqv? m x) (lp m rest (or inex? (inexact? x)))) ;; not for -0. 
0.
               ((= m x) (lp (if (zero? m) 0. m) rest (or inex? (inexact? x)))) 
;; -0. and 0.
               ;; neither > nor < nor = means (nan): nothing more to do
               (else
                (nan)))))))))
    (define-syntax f
      (lambda (stx)
        (syntax-case stx ()
          (f (identifier? #'f) #'%f)
          ((_) #'(error "Wrong number of arguments to" 'wrong-num-args))
          ((_ x) #'(if (real? x) x (error "Wrong type argument" 
'wrong-type-arg)))
          ((_ x y)
           #'(let ((x* x)
                   (y* y)
                   (inex? (or (inexact? x) (inexact? y))))
               (cond ((compare x* y*) (if inex? (exact->inexact x*) x*))
                     ((compare y* x*) (if inex? (exact->inexact y*) y*))
                     ((eqv? x* y*) x*) ;; not for -0. 0.
                     ((= x* y*) (if (zero? x*) 0. x*)) ;; -0. and 0.
                     (else (nan)))))
          ((_ x . y)
           #'(f x (f . y))))))))

(define-minmax-expansion max %max >)
(define-minmax-expansion min %min <)

> I believe this simplifies it a bit too much.  The 2019 version (well,
> the draft, I am not buying the final document, and consider the very
> need to *buy* a standard to be absurd) specifies both `minimum' and
> `minimumNumber' with different behavior regarding NaNs (section 9.6).  I
> do not know whether r7rs prescribes which of those shall be used, but
> probably not.

I don’t think it does.

>> But from r7rs:
>>
>>     An arithmetic operation where one operand is NaN
>>     returns NaN, unless the implementation can prove that the
>>     result would be the same if the NaN were replaced by any
>>     rational number.
>>
>> Are min and max arithmetic operations?
>
> ¯\_(ツ)_/¯

:-)

I think this means: don’t change behavior.

>> Do I need to import something?
>
> Ah, my bad, it is an extension[0] from my library, it helps to make
> large numbers more readable.  It is sourced automatically on REPL start
> and I did not realize I have used it.  You can just drop all of the _,
> including the #_ prefix, and it will work.  Sorry about that.
>
> 0: https://git.wolfsden.cz/guile-wolfsden/tree/wolfsden/x/underscore.scm

No worries, that looks pretty useful.

> In all the above I see large variances in GC time, so I wonder whether
> that had some effect on the result.  In my tests I have always executed
> ,gc first, so you can see the GC is always 0 (min/max should not really
> need to trigger GC anyway).  I am unsure how large the effect of that is
> though, possibly not large.

In the benchmarks, gc made a difference for the very large lists. So it
may have some optimization to do.

Best wishes,
Arne
-- 
Unpolitisch sein
heißt politisch sein,
ohne es zu merken.
draketo.de

Attachment: signature.asc
Description: PGP signature

Reply via email to