From 4c08c496f68217b122c7a89dfa72fa01ca81d845 Mon Sep 17 00:00:00 2001 From: Vincent St-Amour Date: Mon, 16 May 2011 16:17:42 -0400 Subject: [PATCH] Report when exact arithmetic is used inside float expressions, which may make the extra precision useless. original commit: cd027109c9aa71d788764d934992d290bd586a39 --- .../optimizer/close-calls/precision-loss.rkt | 16 ++++++++ collects/typed-scheme/optimizer/float.rkt | 38 +++++++++++++++---- 2 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 collects/tests/typed-scheme/optimizer/close-calls/precision-loss.rkt diff --git a/collects/tests/typed-scheme/optimizer/close-calls/precision-loss.rkt b/collects/tests/typed-scheme/optimizer/close-calls/precision-loss.rkt new file mode 100644 index 00000000..33c9cc92 --- /dev/null +++ b/collects/tests/typed-scheme/optimizer/close-calls/precision-loss.rkt @@ -0,0 +1,16 @@ +#; +( +precision-loss.rkt 13:3 (#%app * (quote 3/4) (quote 2/3)) -- exact arithmetic subexpression inside a float expression, extra precision discarded -- caused by: 13:0 (#%app + (#%app * (quote 3/4) (quote 2/3)) (quote 2.0)) +2.5 +2.75 + ) + +#lang typed/racket + +;; warn when the extra precision gained by doing exact computations would +;; be lost when the results are mixed with floats, resulting in extra +;; computation cost for (usually) no gain +(+ (* 3/4 2/3) ; exact computation + 2.0) ; extra precision lost +(+ 3/4 2.0) ; here, since the exact subexpression is atomic, it will get +;; coerced anyway, so there's not much need for a warning diff --git a/collects/typed-scheme/optimizer/float.rkt b/collects/typed-scheme/optimizer/float.rkt index bb11913a..68775017 100644 --- a/collects/typed-scheme/optimizer/float.rkt +++ b/collects/typed-scheme/optimizer/float.rkt @@ -86,18 +86,42 @@ f2:float-arg-expr fs:float-arg-expr ...) ;; if the result is a float, we can coerce integers to floats and optimize - #:when (let ([safe-to-opt? (subtypeof? this-syntax -Flonum)]) - ;; if we don't have a return type of float, we missed an optimization - ;; opportunity, report it - ;; ignore operations that stay within integers or rationals, since - ;; these have nothing to do with float optimizations - (when (and (not safe-to-opt?) - (in-real-layer? this-syntax)) + #:when (let* ([safe-to-opt? (subtypeof? this-syntax -Flonum)] + ;; if we don't have a return type of float, we missed an optimization + ;; opportunity, report it + ;; ignore operations that stay within integers or rationals, since + ;; these have nothing to do with float optimizations + [close-call? (and (not safe-to-opt?) + (in-real-layer? this-syntax))]) + (when close-call? (log-close-call "binary, args all float-arg-expr, return type not Float" this-syntax (for/first ([x (in-list (syntax->list #'(f1 f2 fs ...)))] #:when (not (subtypeof? x -Flonum))) x))) + ;; If an optimization was expected (whether it was safe or not doesn't matter), + ;; report subexpressions doing expensive exact arithmetic (Exact-Rational and + ;; Real arithmetic), since that extra precision would be "lost" by going to + ;; floating-point in this expression. + ;; Since this exact behavior can be desirable, it's invalid to optimize it away, + ;; but it's more likely to be there by accident. I can't really think of many + ;; use cases for computing exact intermediate results, then converting them to + ;; floats at the end. + (when (or safe-to-opt? close-call?) + (for ([subexpr (in-list (syntax->list #'(f1 f2 fs ...)))] + #:when (or (in-real-layer? subexpr) + (in-rational-layer? subexpr))) + (syntax-parse subexpr + ;; Only warn about subexpressions that actually perform exact arithmetic. + ;; There's not much point in warning about literals/variables that will + ;; be coerced anyway, or about things like: + ;; (vector-ref vector-of-rationals x) + ;; which don't perform arithmetic despite returning numbers. + [(#%plain-app (~var op (float-op binary-float-ops)) xs ...) + (log-close-call + "exact arithmetic subexpression inside a float expression, extra precision discarded" + subexpr this-syntax)] + [_ #f]))) safe-to-opt?) #:with opt (begin (log-optimization "binary float" #'op)