
additions/changes * More accurate `flulp-error' * Added `flonum->fields', `fields->flonum', `flonum->sig+exp', `sig+exp->flonum' (currently undocumented) * Correctly rounded, robust `bigfloat->fl2' and `fl2' * Correctly rounded, robust `fl+/error', `fl-/error', `fl*/error', `flsqr/error', `fl//error' * Much faster but slightly less accurate fl2 ops (shamelessly stolen from crlibm, which is LGPL) * Added `fl2ulp', `fl2ulp-error', `fl2?' (which detects overlap), `+max-fl2-subnormal.0' (which was tricky), `fl2abs' * Added deterministic and randomized flonum op tests (against MPFR) * Added deterministic and randomized flonum/error op tests (against MPFR) * Added deterministic and randomized fl2 op tests (against MPFR) * Exposed FPU tests in `math/utils' (currently undocumented)
419 lines
17 KiB
Racket
419 lines
17 KiB
Racket
#lang typed/racket/base
|
|
|
|
(require racket/math
|
|
racket/flonum
|
|
racket/list
|
|
typed/rackunit
|
|
"../base/base-random.rkt"
|
|
"../flonum/expansion/expansion-base.rkt"
|
|
"../flonum/flonum-functions.rkt"
|
|
"../flonum/flonum-constants.rkt"
|
|
"../flonum/flonum-bits.rkt"
|
|
"../flonum/flonum-error.rkt"
|
|
"../distributions/dist-struct.rkt"
|
|
"../distributions/geometric-dist.rkt"
|
|
"../../bigfloat.rkt")
|
|
|
|
(provide print-test-progress?
|
|
test-fpu-arith
|
|
test-fpu-trig
|
|
test-fpu-non-trig
|
|
test-fpu-arith/error
|
|
test-fpu-arith/fl2
|
|
test-fpu-non-trig/fl2
|
|
test-fpu)
|
|
|
|
;; Allowable error for different kinds of functions, in ulps
|
|
(define flonum-fun-ulps 0.5)
|
|
(define flonum/error-fun-ulps 0.5)
|
|
(define unary-fl2-fun-ulps 1.0)
|
|
(define binary-fl2-fun-ulps 8.0)
|
|
|
|
;; ===================================================================================================
|
|
;; Helpers
|
|
|
|
(: different-zero? (Real Real -> Boolean))
|
|
(define (different-zero? x y)
|
|
(or (and (eqv? x -0.0) (eqv? y 0.0))
|
|
(and (eqv? x 0.0) (eqv? y -0.0))))
|
|
|
|
(: fl2->real* (Flonum Flonum -> Real))
|
|
;; Like `fl2->real', but returns signed flonum zeros
|
|
(define (fl2->real* x2 x1)
|
|
(define x.0 (fl+ x2 x1))
|
|
(cond [(zero? x.0) x2]
|
|
[else (fl2->real x2 x1)]))
|
|
|
|
(: bigfloat->real* (Bigfloat -> Real))
|
|
;; Like `bigfloat->real*', but returns a signed infinity or a signed flonum zero if conversion would
|
|
;; overflow or underflow a flonum
|
|
(define (bigfloat->real* x)
|
|
(define x.0 (bigfloat->flonum x))
|
|
(cond [(fl= x.0 0.0) x.0]
|
|
[(flrational? x.0) (bigfloat->real x)]
|
|
[else x.0]))
|
|
|
|
(: filter/ulp-error (All (A B) ((Listof (List A (U B Flonum)))
|
|
Flonum -> (Listof (List A (U B Flonum))))))
|
|
(define (filter/ulp-error xes ulps)
|
|
(filter (λ: ([xe : (List A (U B Flonum))])
|
|
(define e (second xe))
|
|
(or (not (flonum? e)) (e . fl> . ulps)))
|
|
xes))
|
|
|
|
(: print-test-progress? (Parameterof Boolean))
|
|
(define print-test-progress? (make-parameter #t))
|
|
|
|
(define progress-chunk-size 200)
|
|
(define progress-superchunk-chunks 5)
|
|
|
|
(: maybe-print-progress (Symbol Integer Natural -> Void))
|
|
(define (maybe-print-progress name i m)
|
|
(when (and (print-test-progress?) (i . > . 0) (i . <= . m))
|
|
(let* ([flush? (cond [(= i 1) (printf "~a: " name)]
|
|
[else #f])]
|
|
[flush? (cond [(= 0 (modulo i progress-chunk-size))
|
|
(cond [(= 0 (modulo i (* progress-superchunk-chunks
|
|
progress-chunk-size)))
|
|
(printf "* ~a " i)]
|
|
[else (printf "*")])]
|
|
[else flush?])]
|
|
[flush? (cond [(= i m) (printf "* ~a~n" m)]
|
|
[else flush?])])
|
|
(when flush? (flush-output)))))
|
|
|
|
;; ===================================================================================================
|
|
;; Test case generation
|
|
|
|
;; Deteriministic test cases
|
|
|
|
(define standard-xs
|
|
(list
|
|
;; Test the sign of the return value of `flexpt'
|
|
-1001.0 -10.0 -0.1 +0.1 +10.0 +1001.0
|
|
;; Test squaring
|
|
(- (flsqrt +min.0)) (flsqrt +min.0)
|
|
(- (flsqrt +max-subnormal.0)) (flsqrt +max-subnormal.0)
|
|
(- (flsqrt +max-fl2-subnormal.0)) (flsqrt +max-fl2-subnormal.0)
|
|
(- (flsqrt +max.0)) (flsqrt +max.0)
|
|
;; Test exp limits
|
|
(fllog +min.0) (fllog +max-subnormal.0) (fllog +max-fl2-subnormal.0) (fllog +max.0)
|
|
;; Standard special values
|
|
-inf.0 -max.0 -1.0 -max-fl2-subnormal.0 -max-subnormal.0 -min.0 -0.0
|
|
+inf.0 +max.0 +1.0 +max-fl2-subnormal.0 +max-subnormal.0 +min.0 +0.0
|
|
+nan.0))
|
|
|
|
(define standard-rs
|
|
(append standard-xs
|
|
(list +10 +1 +1/7 +1/10 +1/13
|
|
-10 -1 -1/7 -1/10 -1/13
|
|
0)))
|
|
|
|
(: product (All (A B) ((Listof A) (Listof B) -> (Values (Listof A) (Listof B)))))
|
|
(define (product as bs)
|
|
(define abs
|
|
(append*
|
|
(for/list: : (Listof (Listof (Pair A B))) ([a (in-list as)])
|
|
(for/list: : (Listof (Pair A B)) ([b (in-list bs)])
|
|
(cons a b)))))
|
|
(values (map (inst car A B) abs) (map (inst cdr A B) abs)))
|
|
|
|
;; Random test cases
|
|
|
|
(define min-subnormal-ord (flonum->ordinal -max-subnormal.0))
|
|
(define max-subnormal-ord (+ 1 (flonum->ordinal +max-subnormal.0)))
|
|
|
|
(define min-fl2-subnormal-ord (flonum->ordinal -max-fl2-subnormal.0))
|
|
(define max-fl2-subnormal-ord (+ 1 (flonum->ordinal +max-fl2-subnormal.0)))
|
|
|
|
(: sample-flonum (case-> (Integer -> (Listof Flonum))
|
|
(Integer Flonum Flonum -> (Listof Flonum))))
|
|
(define (sample-flonum n [mn -inf.0] [mx +inf.0])
|
|
(define min-ord (flonum->ordinal mn))
|
|
(define max-ord (+ 1 (flonum->ordinal mx)))
|
|
(let ([min-subnormal-ord (max min-ord min-subnormal-ord)]
|
|
[max-subnormal-ord (min max-ord max-subnormal-ord)]
|
|
[min-fl2-subnormal-ord (max min-ord min-fl2-subnormal-ord)]
|
|
[max-fl2-subnormal-ord (min max-ord max-fl2-subnormal-ord)])
|
|
(build-list
|
|
n (λ (_)
|
|
(define r (random))
|
|
(ordinal->flonum
|
|
(cond [(and (min-subnormal-ord . < . max-subnormal-ord) (r . < . 0.1))
|
|
(random-integer min-subnormal-ord max-subnormal-ord)]
|
|
[(and (min-fl2-subnormal-ord . < . max-fl2-subnormal-ord) (r . < . 0.2))
|
|
(random-integer min-subnormal-ord max-subnormal-ord)]
|
|
[else
|
|
(random-integer min-ord max-ord)]))))))
|
|
|
|
(define denom-dist (geometric-dist 1e-32))
|
|
|
|
(: sample-rational (Integer -> (Listof Exact-Rational)))
|
|
(define (sample-rational n)
|
|
(map (λ: ([f1 : Flonum] [d : Integer])
|
|
(+ (inexact->exact f1)
|
|
(* (if ((random) . > . 0.5) -1 1)
|
|
(/ (random-natural (+ 1 d)) d)
|
|
(expt 2 (- (exact-round (/ (fllog (flabs f1)) (fllog 2.0))) 52)))))
|
|
(sample-flonum n)
|
|
(map (λ: ([x : Flonum]) (+ 1 (exact-floor x))) (sample denom-dist n))))
|
|
|
|
;; ===================================================================================================
|
|
;; Flonum functions
|
|
|
|
(: flonum-error (Flonum Bigfloat -> Any))
|
|
(define (flonum-error z z0.bf)
|
|
(define z0 (bigfloat->real* z0.bf))
|
|
(cond [(different-zero? z z0) (list 'different-zero? z z0)]
|
|
[else (flulp-error z z0)]))
|
|
|
|
(: unary-flonum-fun-error ((Flonum -> Flonum) (Bigfloat -> Bigfloat) Flonum -> Any))
|
|
(define (unary-flonum-fun-error f g x)
|
|
(flonum-error (f x) (parameterize ([bf-precision 53])
|
|
(g (bf x)))))
|
|
|
|
(: test-unary-flonum-fun
|
|
(Symbol (Flonum -> Flonum) (Bigfloat -> Bigfloat) Integer Flonum Flonum
|
|
-> (Listof (List (List Symbol Flonum) Any))))
|
|
(define (test-unary-flonum-fun name f g n mn mx)
|
|
(define xs (append standard-xs (sample-flonum n mn mx)))
|
|
(define m (length xs))
|
|
(filter/ulp-error
|
|
(for/list: : (Listof (List (List Symbol Flonum) Any)) ([x (in-list xs)]
|
|
[i (in-naturals 1)])
|
|
(maybe-print-progress name i m)
|
|
(list (list name x) (unary-flonum-fun-error f g x)))
|
|
flonum-fun-ulps))
|
|
|
|
(: binary-flonum-fun-error
|
|
((Flonum Flonum -> Flonum) (Bigfloat Bigfloat -> Bigfloat) Flonum Flonum -> Any))
|
|
(define (binary-flonum-fun-error f g x y)
|
|
(flonum-error (f x y) (parameterize ([bf-precision 53])
|
|
(g (bf x) (bf y)))))
|
|
|
|
(: test-binary-flonum-fun
|
|
(Symbol (Flonum Flonum -> Flonum) (Bigfloat Bigfloat -> Bigfloat) Integer
|
|
-> (Listof (List (List Symbol Flonum Flonum) Any))))
|
|
(define (test-binary-flonum-fun name f g n)
|
|
(define-values (pre-xs pre-ys) (product standard-xs standard-xs))
|
|
(define xs (append pre-xs (sample-flonum n)))
|
|
(define ys (append pre-ys (sample-flonum n)))
|
|
(define m (length xs))
|
|
(filter/ulp-error
|
|
(for/list: : (Listof (List (List Symbol Flonum Flonum) Any)) ([x (in-list xs)]
|
|
[y (in-list ys)]
|
|
[i (in-naturals 1)])
|
|
(maybe-print-progress name i m)
|
|
(list (list name x y) (binary-flonum-fun-error f g x y)))
|
|
flonum-fun-ulps))
|
|
|
|
;; ===================================================================================================
|
|
;; fl2 conversion
|
|
|
|
(: fl2-error (Flonum Flonum Real -> Any))
|
|
(define (fl2-error x2 x1 x)
|
|
(cond [(not (fl2? x2 x1)) (list 'not-fl2? x2 x1)]
|
|
[(different-zero? x2 x) (list 'different-zero? x2 x)]
|
|
[else (fl2ulp-error x2 x1 x)]))
|
|
|
|
(: fl2-conversion-error (Real -> Any))
|
|
(define (fl2-conversion-error x)
|
|
(define-values (x2 x1) (fl2 x))
|
|
(fl2-error x2 x1 x))
|
|
|
|
(: test-fl2-conversion (Integer -> (Listof (List (List 'fl2 Real) Any))))
|
|
(define (test-fl2-conversion n)
|
|
(define xs (append standard-rs (sample-rational n)))
|
|
(define m (length xs))
|
|
(filter/ulp-error
|
|
(for/list: : (Listof (List (List 'fl2 Real) Any)) ([x (in-list xs)]
|
|
[i (in-naturals 1)])
|
|
(maybe-print-progress 'fl2 i m)
|
|
(list (list 'fl2 x) (fl2-conversion-error x)))
|
|
flonum/error-fun-ulps))
|
|
|
|
;; ===================================================================================================
|
|
;; Flonum arithmetic with error
|
|
|
|
(: unary-flonum/error-fun-error ((Flonum -> (Values Flonum Flonum)) (Bigfloat -> Bigfloat) Flonum
|
|
-> Any))
|
|
(define (unary-flonum/error-fun-error f g x)
|
|
(define-values (z2 z1) (f x))
|
|
(fl2-error z2 z1 (parameterize ([bf-precision 256])
|
|
(bigfloat->real* (g (bf x))))))
|
|
|
|
(: binary-flonum/error-fun-error ((Flonum Flonum -> (Values Flonum Flonum))
|
|
(Bigfloat Bigfloat -> Bigfloat)
|
|
Flonum Flonum
|
|
-> Any))
|
|
(define (binary-flonum/error-fun-error f g x y)
|
|
(define-values (z2 z1) (f x y))
|
|
(fl2-error z2 z1 (parameterize ([bf-precision 256])
|
|
(bigfloat->real* (g (bf x) (bf y))))))
|
|
|
|
(: test-unary-flonum/error-fun
|
|
(Symbol (Flonum -> (Values Flonum Flonum)) (Bigfloat -> Bigfloat) Integer
|
|
-> (Listof (List (List Symbol Flonum) Any))))
|
|
(define (test-unary-flonum/error-fun name f g n)
|
|
(define xs (append standard-xs (sample-flonum n)))
|
|
(define m (length xs))
|
|
(filter/ulp-error
|
|
(for/list: : (Listof (List (List Symbol Flonum) Any)) ([x (in-list xs)]
|
|
[i (in-naturals 1)])
|
|
(maybe-print-progress name i m)
|
|
(list (list name x) (unary-flonum/error-fun-error f g x)))
|
|
flonum/error-fun-ulps))
|
|
|
|
(: test-binary-flonum/error-fun
|
|
(Symbol (Flonum Flonum -> (Values Flonum Flonum)) (Bigfloat Bigfloat -> Bigfloat) Integer
|
|
-> (Listof (List (List Symbol Flonum Flonum) Any))))
|
|
(define (test-binary-flonum/error-fun name f g n)
|
|
(define-values (pre-xs pre-ys) (product standard-xs standard-xs))
|
|
(define xs (append pre-xs (sample-flonum n)))
|
|
(define ys (append pre-ys (sample-flonum n)))
|
|
(define m (length xs))
|
|
(filter/ulp-error
|
|
(for/list: : (Listof (List (List Symbol Flonum Flonum) Any)) ([x (in-list xs)]
|
|
[y (in-list ys)]
|
|
[i (in-naturals 1)])
|
|
(maybe-print-progress name i m)
|
|
(list (list name x y) (binary-flonum/error-fun-error f g x y)))
|
|
flonum/error-fun-ulps))
|
|
|
|
;; ===================================================================================================
|
|
;; Flonum expansions
|
|
|
|
(: unary-fl2-fun-error ((Flonum Flonum -> (Values Flonum Flonum)) (Bigfloat -> Bigfloat)
|
|
Flonum Flonum -> Any))
|
|
(define (unary-fl2-fun-error f g x2 x1)
|
|
(define-values (z2 z1) (f x2 x1))
|
|
(fl2-error z2 z1 (parameterize ([bf-precision 256])
|
|
(bigfloat->real* (g (bf (fl2->real* x2 x1)))))))
|
|
|
|
(: test-unary-fl2-fun
|
|
(Symbol (Flonum Flonum -> (Values Flonum Flonum)) (Bigfloat -> Bigfloat) Integer
|
|
-> (Listof (List (List Symbol Flonum Flonum) Any))))
|
|
(define (test-unary-fl2-fun name f g n)
|
|
(define xs (append standard-rs (sample-rational n)))
|
|
(define m (length xs))
|
|
(filter/ulp-error
|
|
(for/list: : (Listof (List (List Symbol Flonum Flonum) Any)) ([x (in-list xs)]
|
|
[i (in-naturals 1)])
|
|
(maybe-print-progress name i m)
|
|
(define-values (x2 x1) (fl2 x))
|
|
(list (list name x2 x1) (unary-fl2-fun-error f g x2 x1)))
|
|
unary-fl2-fun-ulps))
|
|
|
|
(: binary-fl2-fun-error ((Flonum Flonum Flonum Flonum -> (Values Flonum Flonum))
|
|
(Bigfloat Bigfloat -> Bigfloat)
|
|
Flonum Flonum Flonum Flonum
|
|
-> Any))
|
|
(define (binary-fl2-fun-error f g x2 x1 y2 y1)
|
|
(define-values (z2 z1) (f x2 x1 y2 y1))
|
|
(fl2-error z2 z1 (parameterize ([bf-precision 256])
|
|
(bigfloat->real* (g (bf (fl2->real* x2 x1)) (bf (fl2->real* y2 y1)))))))
|
|
|
|
(: test-binary-fl2-fun
|
|
(Symbol (Flonum Flonum Flonum Flonum -> (Values Flonum Flonum)) (Bigfloat Bigfloat -> Bigfloat)
|
|
Integer -> (Listof (List (List Symbol Flonum Flonum Flonum Flonum) Any))))
|
|
(define (test-binary-fl2-fun name f g n)
|
|
(define-values (pre-xs pre-ys) (product standard-rs standard-rs))
|
|
(define xs (append pre-xs (sample-rational n)))
|
|
(define ys (append pre-ys (sample-rational n)))
|
|
(define m (length xs))
|
|
(filter/ulp-error
|
|
(for/list: : (Listof (List (List Symbol Flonum Flonum Flonum Flonum) Any)
|
|
) ([x (in-list xs)]
|
|
[y (in-list ys)]
|
|
[i (in-naturals 1)])
|
|
(maybe-print-progress name i m)
|
|
(define-values (x2 x1) (fl2 x))
|
|
(define-values (y2 y1) (fl2 y))
|
|
(list (list name x2 x1 y2 y1) (binary-fl2-fun-error f g x2 x1 y2 y1)))
|
|
binary-fl2-fun-ulps))
|
|
|
|
;; ===================================================================================================
|
|
|
|
(: test-fpu-arith (Natural -> Any))
|
|
(define (test-fpu-arith n)
|
|
(check-equal? (test-unary-flonum-fun 'flabs flabs bfabs n -inf.0 +inf.0)
|
|
'())
|
|
(check-equal? (test-binary-flonum-fun 'fl+ fl+ bf+ n)
|
|
'())
|
|
(check-equal? (test-binary-flonum-fun 'fl- fl- bf- n)
|
|
'())
|
|
(check-equal? (test-binary-flonum-fun 'fl* fl* bf* n)
|
|
'())
|
|
(check-equal? (test-binary-flonum-fun 'fl/ fl/ bf/ n)
|
|
'()))
|
|
|
|
(: test-fpu-trig (Natural -> Any))
|
|
(define (test-fpu-trig n)
|
|
(check-equal? (test-unary-flonum-fun 'flsin flsin bfsin n -inf.0 +inf.0)
|
|
'())
|
|
(check-equal? (test-unary-flonum-fun 'flcos flcos bfcos n -inf.0 +inf.0)
|
|
'())
|
|
(check-equal? (test-unary-flonum-fun 'fltan fltan bftan n -inf.0 +inf.0)
|
|
'())
|
|
(check-equal? (test-unary-flonum-fun 'flasin flasin bfasin n -1.0 1.0)
|
|
'())
|
|
(check-equal? (test-unary-flonum-fun 'flacos flacos bfacos n -1.0 1.0)
|
|
'())
|
|
(check-equal? (test-unary-flonum-fun 'flatan flatan bfatan n -inf.0 +inf.0)
|
|
'()))
|
|
|
|
(: test-fpu-non-trig (Natural -> Any))
|
|
(define (test-fpu-non-trig n)
|
|
(check-equal? (test-unary-flonum-fun 'flsqrt flsqrt bfsqrt n 0.0 +inf.0)
|
|
'())
|
|
(check-equal? (test-unary-flonum-fun 'fllog fllog bflog n 0.0 +inf.0)
|
|
'())
|
|
(check-equal? (test-unary-flonum-fun 'flexp flexp bfexp n -746.0 710.0)
|
|
'())
|
|
(check-equal? (test-binary-flonum-fun 'flexpt flexpt bfexpt n)
|
|
'()))
|
|
|
|
(: test-fpu-arith/error (Natural -> Any))
|
|
(define (test-fpu-arith/error n)
|
|
(check-equal? (test-binary-flonum/error-fun 'fl+/error fl+/error bf+ n)
|
|
'())
|
|
(check-equal? (test-binary-flonum/error-fun 'fl-/error fl-/error bf- n)
|
|
'())
|
|
(check-equal? (test-binary-flonum/error-fun 'fl*/error fl*/error bf* n)
|
|
'())
|
|
(check-equal? (test-unary-flonum/error-fun 'flsqr/error flsqr/error bfsqr n)
|
|
'())
|
|
(check-equal? (test-binary-flonum/error-fun 'fl//error fl//error bf/ n)
|
|
'()))
|
|
|
|
(: test-fpu-arith/fl2 (Natural -> Any))
|
|
(define (test-fpu-arith/fl2 n)
|
|
(check-equal? (test-fl2-conversion n)
|
|
'())
|
|
(check-equal? (test-unary-fl2-fun 'fl2abs fl2abs bfabs n)
|
|
'())
|
|
(check-equal? (test-binary-fl2-fun 'fl2+ fl2+ bf+ n)
|
|
'())
|
|
(check-equal? (test-binary-fl2-fun 'fl2- fl2- bf- n)
|
|
'())
|
|
(check-equal? (test-binary-fl2-fun 'fl2* fl2* bf* n)
|
|
'())
|
|
(check-equal? (test-unary-fl2-fun 'fl2sqr fl2sqr bfsqr n)
|
|
'())
|
|
(check-equal? (test-binary-fl2-fun 'fl2/ fl2/ bf/ n)
|
|
'()))
|
|
|
|
(: test-fpu-non-trig/fl2 (Natural -> Any))
|
|
(define (test-fpu-non-trig/fl2 n)
|
|
(check-equal? (test-unary-fl2-fun 'fl2sqrt fl2sqrt bfsqrt n)
|
|
'()))
|
|
|
|
(: test-fpu (Natural -> Any))
|
|
(define (test-fpu n)
|
|
(test-fpu-arith n)
|
|
(test-fpu-trig n)
|
|
(test-fpu-non-trig n)
|
|
(test-fpu-arith/error n)
|
|
(test-fpu-arith/fl2 n)
|
|
(test-fpu-non-trig/fl2 n))
|