From 894a44e1b8aa86523c9bf7c76eaab435e0341d2c Mon Sep 17 00:00:00 2001 From: Sam Tobin-Hochstadt Date: Mon, 9 Mar 2026 11:57:02 -0400 Subject: [PATCH] fix complex `exp` optimization Found in https://drdr.racket-lang.org/72369/racket/share/pkgs/typed-racket-test/external/tr-random-testing.rkt --- .../typed-racket/optimizer/float-complex.rkt | 13 +++++++++++-- typed-racket-test/optimizer/known-bugs.rkt | 8 +++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/typed-racket-lib/typed-racket/optimizer/float-complex.rkt b/typed-racket-lib/typed-racket/optimizer/float-complex.rkt index 2ff7d3bd5..b5a2b99b5 100644 --- a/typed-racket-lib/typed-racket/optimizer/float-complex.rkt +++ b/typed-racket-lib/typed-racket/optimizer/float-complex.rkt @@ -261,10 +261,19 @@ #:with scaling-factor (generate-temporary "unboxed-scaling-") #:do [(log-unboxing-opt "unboxed unary float complex")] #:with (bindings ...) + ;; exp(a+bi) = exp(a) * (cos(b) + i*sin(b)) + ;; When b is ±0.0, sin(b) is ±0.0 and exp(a) may be +inf.0, + ;; so sin(b)*exp(a) would produce NaN via IEEE 754 (0*inf=NaN). + ;; In Racket: (exp +inf.0+0.0i) = +inf.0+0.0i, (exp +nan.0+0.0i) = +nan.0+0.0i. + ;; Guard the zero case to avoid the spurious NaN. #`(c.bindings ... ((scaling-factor) (unsafe-flexp c.real-binding)) - ((real-binding) (unsafe-fl* (unsafe-flcos c.imag-binding) scaling-factor)) - ((imag-binding) (unsafe-fl* (unsafe-flsin c.imag-binding) scaling-factor)))) + ((real-binding) (if (unsafe-fl= c.imag-binding 0.0) + scaling-factor + (unsafe-fl* (unsafe-flcos c.imag-binding) scaling-factor))) + ((imag-binding) (if (unsafe-fl= c.imag-binding 0.0) + c.imag-binding + (unsafe-fl* (unsafe-flsin c.imag-binding) scaling-factor))))) ;; we can eliminate boxing that was introduced by the user diff --git a/typed-racket-test/optimizer/known-bugs.rkt b/typed-racket-test/optimizer/known-bugs.rkt index 42f7ead9a..f3953e133 100644 --- a/typed-racket-test/optimizer/known-bugs.rkt +++ b/typed-racket-test/optimizer/known-bugs.rkt @@ -111,7 +111,13 @@ (good-opt (- (expt 10 309) +inf.0)) ;; make-polar with NaN should return complex NaN, not real NaN - (good-opt (+ 1.0 (make-polar +nan.0 1.0))))) + (good-opt (+ 1.0 (make-polar +nan.0 1.0))) + + ;; exp with infinite real part and zero imaginary part should not produce NaN + ;; (inf * 0 = NaN in IEEE 754, but exp(+inf+0i) = +inf+0i per C99) + (good-opt (exp (make-rectangular +inf.0 -0.0))) + (good-opt (exp (make-rectangular +inf.0 0.0))) + (good-opt (exp (make-rectangular +nan.0 0.0))))) (module+ main (require rackunit/text-ui)