Skip to content

Commit ca4a187

Browse files
committed
Fix bugs in floating point optimizations
1. Fix type of (/ 0.0): Changed from (-> -NonPosReal -NonPosReal) to (-> -NegReal -NonPosReal) to correctly handle (/ (min 0.0 0)). 2. Avoid optimizing float exprs when conversion can change result: Added safe-to-convert? check to prevent converting large exact numbers to infinity before operations. 3. Fix float-complex multiplication crash with exact integer operands: The imaginary part calculation was using unsafe-fl* with exact integers when non-float optimization preserved exact arithmetic. Based on PR racket#1381. Fixes racket#1042.
1 parent 3caf478 commit ca4a187

4 files changed

Lines changed: 36 additions & 8 deletions

File tree

typed-racket-lib/typed-racket/base-env/base-env-numeric.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@
12481248
(varop-1+ -InexactReal)
12491249
;; reals
12501250
(varop-1+ -PosReal -NonNegReal)
1251-
(-> -NonPosReal -NonPosReal)
1251+
(-> -NegReal -NonPosReal)
12521252
(-> -NegReal -NegReal -NonNegReal) ; 0.0 is non-neg, but doesn't preserve sign
12531253
(-> -NegReal -PosReal -NonPosReal) ; idem
12541254
(-> -PosReal -NegReal -NonPosReal) ; idem

typed-racket-lib/typed-racket/optimizer/float-complex.rkt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,21 @@
208208
(define new-imag-id (if both-real?
209209
(mark-as-real (car is))
210210
(car is)))
211+
;; Helper to convert non-float values to flonum for unsafe ops
212+
(define (maybe-to-float v)
213+
(if (as-non-float v)
214+
#`(real->double-flonum #,v)
215+
v))
211216
(loop (car rs) new-imag-id (cdr e1) (cdr e2) (cdr rs) (cdr is)
212217
;; complex multiplication, imag part, then real part (reverse)
213218
;; we eliminate operations on the imaginary parts of reals
214219
(list* #`((#,new-imag-id)
215220
#,(cond ((and o-real? e-real?) #'0.0)
216-
(o-real? #`(unsafe-fl* #,o1 #,(car e2)))
217-
(e-real? #`(unsafe-fl* #,o2 #,(car e1)))
221+
(o-real? #`(unsafe-fl* #,(maybe-to-float o1) #,(car e2)))
222+
(e-real? #`(unsafe-fl* #,o2 #,(maybe-to-float (car e1))))
218223
(else
219-
#`(unsafe-fl+ (unsafe-fl* #,o2 #,(car e1))
220-
(unsafe-fl* #,o1 #,(car e2))))))
224+
#`(unsafe-fl+ (unsafe-fl* #,o2 #,(maybe-to-float (car e1)))
225+
(unsafe-fl* #,(maybe-to-float o1) #,(car e2))))))
221226
#`((#,(car rs))
222227
#,(cond [(and o-nf e-nf both-real?)
223228
;; we haven't seen float operands yet, so

typed-racket-lib/typed-racket/optimizer/float.rkt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@
112112
(not (subtypeof? stx -SingleFlonum))
113113
(not (subtypeof? stx -Int))))
114114

115+
;; Check whether an expression can be safely converted to flonum without
116+
;; changing the result (e.g., a large finite number becoming infinity)
117+
(define (safe-to-convert? a)
118+
(or (subtypeof? a -Fixnum)
119+
(syntax-parse a #:literals (quote)
120+
[(quote n) #:when (flonum? (syntax-e #'n)) #t]
121+
[(quote n) #:when (rational? (real->double-flonum (syntax-e #'n))) #t]
122+
[_ #f])))
115123

116124
(define-syntax-class float-opt-expr
117125
#:commit
@@ -144,8 +152,11 @@
144152
(and (subtypeof? this-syntax -Flonum)
145153
(for/and ([a (in-syntax #'(fs ...))])
146154
;; flonum or provably non-zero
155+
;; also need to make sure that coercing to float won't change
156+
;; a large finite number to infinity, altering the result
147157
(or (subtypeof? a -Flonum)
148-
(subtypeof? a (Un -PosReal -NegReal))))
158+
(and (subtypeof? a (Un -PosReal -NegReal))
159+
(safe-to-convert? a))))
149160
(>= 1
150161
(for/sum ([a (in-syntax #'(fs ...))]
151162
#:when (not (subtypeof? a -Flonum)))

typed-racket-test/optimizer/known-bugs.rkt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
(define (mk-eval lang)
2020
(call-with-trusted-sandbox-configuration
2121
(λ ()
22-
(parameterize ([sandbox-memory-limit 300])
22+
(parameterize ([sandbox-memory-limit 3000])
2323
(make-evaluator lang)))))
2424
(define racket-eval (mk-eval 'racket))
2525
(define tr-eval (mk-eval 'typed/racket))
@@ -85,6 +85,9 @@
8585
;; Multiplication of multiple args should keep exact semantics for exact args
8686
(good-opt (* (expt 10 500) (expt 10 -500) 1.0+1.0i))
8787

88+
;; Multiplication with multiple exact reals and a float complex should not crash
89+
(good-opt (* 2 3 1.0+1.0i))
90+
8891
;; Addition of multiple args should keep exact semantics for exact args
8992
(good-opt (+ (expt 10 501) (expt -10 501) 1.0+1.0i))
9093

@@ -99,7 +102,16 @@
99102
(good-opt (conjugate 0.0+0.0i))
100103

101104
;; Magnitude should always return positive results
102-
(good-opt (magnitude -1.0-2i))))
105+
(good-opt (magnitude -1.0-2i))
106+
107+
;; Division of 0.0 should return correct sign
108+
(good-opt (/ (min 0.0 0)))
109+
110+
;; Subtraction should not convert large numbers to infinity prematurely
111+
(good-opt (- (expt 10 309) +inf.0))
112+
113+
;; make-polar with NaN should return complex NaN, not real NaN
114+
(good-opt (+ 1.0 (make-polar +nan.0 1.0)))))
103115

104116
(module+ main
105117
(require rackunit/text-ui)

0 commit comments

Comments
 (0)