[racket-dev] What are single flonums good for?
I ask because I'm tired of worrying about them. More precisely, I'm tired of Typed Racket (rightly) making me worry about them. I'm also a little bit tired of this: (: foo (case- (Single-Flonum - Single-Flonum) (Flonum - Flonum) (Real - Real))) (define (foo x) (cond [(double-flonum? x) (flfoo x)] [(single-flonum? x) (real-single-flonum (flfoo (real-double-flonum x)))] [else (flfoo (real-double-flonum x))])) I'm annoyed by the prospect of doing something similar to subtypes of Real-Distribution. It's nice right now that a probability is always a Flonum. Changing it would make things slower, more complicated, and more error-prone. They can't be for speed. I just ran some tests. With TR's optimizer off or in untyped Racket, the performance gain using single flonums is negligible. With TR's optimizer on, doubles are at least twice as fast. Compatibility with old code? No, they were enabled in 5.1.1. Compatibility with C code? Why not have the FFI convert them? Save space? I can see that. It won't help much if they're sent to math library functions, though. Those will convert them to flonums and usually box the converted values. They make it easy to write wrong code, because it's easy to use `exact-inexact' when you really should use `real-double-flonum'. Plot, for example, fails to render functions that return single flonums. I'm surprised it doesn't segfault. I'm sure plot isn't the only one. Every use of `exact-inexact' in the standard library is suspect. If followed by applying an unsafe op, it's wrong. Why do we have these things? Neil ⊥ _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] What are single flonums good for?
Single-precision float support used to be enabled via a configure option, which meant that some Racket installations would support them, and some would not. Since zo files are meant to be portable, they could not contain single-precision floats. So, compilation would promote single literals to doubles. This meant that compilation could change the behavior of a program. Here's an example: stamourv@ahuntsic:small-float-test$ cat test2.rkt #lang racket (define (f x) (displayln (flonum? x))) (f (if (with-input-from-string #t read) 1.0f0 2.0f0)) stamourv@ahuntsic:small-float-test$ racket test2.rkt #f stamourv@ahuntsic:small-float-test$ raco make test2.rkt stamourv@ahuntsic:small-float-test$ racket test2.rkt #t stamourv@ahuntsic:small-float-test$ This example has to be a bit convoluted to defeat constant folding, which makes the problem disappear: stamourv@ahuntsic:small-float-test$ cat test.rkt #lang racket (flonum? 1.0f0) stamourv@ahuntsic:small-float-test$ racket test.rkt #f stamourv@ahuntsic:small-float-test$ raco make test.rkt stamourv@ahuntsic:small-float-test$ racket test.rkt #f stamourv@ahuntsic:small-float-test$ raco decompile test.rkt (begin (module test (#%apply-values |_print-values@(lib racket/private/modbeg.rkt)| '#f))) stamourv@ahuntsic:small-float-test$ This can make for pretty elusive bugs. This gets especially problematic when you add unsafe float operations to the mix, which can turn these issues into segfaults. The solution we picked was to support single-precision floats by default and to allow them in zos, which makes these discrepancies go away. I agree that having to handle single floats when reasoning about numbers complicates things, and it annoys me too. But I still think it's less problematic than what I describe above. At Wed, 12 Sep 2012 08:31:29 -0600, Neil Toronto wrote: I ask because I'm tired of worrying about them. More precisely, I'm tired of Typed Racket (rightly) making me worry about them. I'm also a little bit tired of this: (: foo (case- (Single-Flonum - Single-Flonum) (Flonum - Flonum) (Real - Real))) (define (foo x) (cond [(double-flonum? x) (flfoo x)] [(single-flonum? x) (real-single-flonum (flfoo (real-double-flonum x)))] [else (flfoo (real-double-flonum x))])) This function already converts rationals to doubles, and it seems `flfoo' produces doubles too. You could drop the second clause, always produce doubles, change the type to `(Real - Flonum)' and leave the conversion to single to the client. Since the math library always operates on doubles internally anyway, this would also eliminate unnecessary conversions to singles between stages of a pipeline. They make it easy to write wrong code, because it's easy to use `exact-inexact' when you really should use `real-double-flonum'. Plot, for example, fails to render functions that return single flonums. I'm surprised it doesn't segfault. Good point, that's a problem. I'm sure plot isn't the only one. Every use of `exact-inexact' in the standard library is suspect. If followed by applying an unsafe op, it's wrong. Why do we have these things? I don't know why they were added originally (as an option). In my limited experience, I don't think I've seen non-test code that uses them. Vincent _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] What are single flonums good for?
On Sep 12, 2012, at 12:24 PM, Vincent St-Amour wrote: Single-precision float support used to be enabled via a configure option, which meant that some Racket installations would support them, and some would not. Since zo files are meant to be portable, they could not contain single-precision floats. So, compilation would promote single literals to doubles. This meant that compilation could change the behavior of a program. ... The solution we picked was to support single-precision floats by default and to allow them in zos, which makes these discrepancies go away. I agree that having to handle single floats when reasoning about numbers complicates things, and it annoys me too. But I still think it's less problematic than what I describe above. Would it be even less problematic to get rid of them entirely, except in FFI? Stephen Bloch sbl...@adelphi.edu _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] What are single flonums good for?
On 09/12/2012 10:24 AM, Vincent St-Amour wrote: I agree that having to handle single floats when reasoning about numbers complicates things, and it annoys me too. But I still think it's less problematic than what I describe above [compilation could change the behavior of a program]. Interesting! I think the least problematic solution would be to separate them from the rest of the numeric tower, and give them their own set of single-flonum-only functions: sf+, sfabs, etc. They could even operate on single-precision complex numbers. Hindsight, etc., though. (: foo (case- (Single-Flonum - Single-Flonum) (Flonum - Flonum) (Real - Real))) (define (foo x) (cond [(double-flonum? x) (flfoo x)] [(single-flonum? x) (real-single-flonum (flfoo (real-double-flonum x)))] [else (flfoo (real-double-flonum x))])) This function already converts rationals to doubles, and it seems `flfoo' produces doubles too. You could drop the second clause, always produce doubles, change the type to `(Real - Flonum)' and leave the conversion to single to the client. Since the math library always operates on doubles internally anyway, this would also eliminate unnecessary conversions to singles between stages of a pipeline. It's not just slower. The unnecessary conversions lose precision because of double rounding. I hadn't thought of that yet, so thanks. Why do we have these things? I don't know why they were added originally (as an option). In my limited experience, I don't think I've seen non-test code that uses them. This gets at the design decision I'm facing now. Would anybody care if the math library just treated them like other non-double-flonum reals, and made no effort to preserve single-flonum-ness? I'm leaning toward doing that now. Neil ⊥ _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] What are single flonums good for?
Two hours ago, Stephen Bloch wrote: Would it be even less problematic to get rid of them entirely, except in FFI? (They're not really an issue for the FFI, since you'd just translate the numbers to them when needed. IOW, racket doesn't even need exact integers to have an FFI with them.) -- ((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay: http://barzilay.org/ Maze is Life! _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] What are single flonums good for?
On Wed, Sep 12, 2012 at 11:24 AM, Vincent St-Amour stamo...@ccs.neu.edu wrote: Single-precision float support used to be enabled via a configure option, which meant that some Racket installations would support them, and some would not. Since zo files are meant to be portable, they could not contain single-precision floats. So, compilation would promote single literals to doubles. This meant that compilation could change the behavior of a program. Here's an example: stamourv@ahuntsic:small-float-test$ cat test2.rkt #lang racket (define (f x) (displayln (flonum? x))) (f (if (with-input-from-string #t read) 1.0f0 2.0f0)) stamourv@ahuntsic:small-float-test$ racket test2.rkt #f stamourv@ahuntsic:small-float-test$ raco make test2.rkt stamourv@ahuntsic:small-float-test$ racket test2.rkt #t stamourv@ahuntsic:small-float-test$ This example has to be a bit convoluted to defeat constant folding, which makes the problem disappear: stamourv@ahuntsic:small-float-test$ cat test.rkt #lang racket (flonum? 1.0f0) stamourv@ahuntsic:small-float-test$ racket test.rkt #f stamourv@ahuntsic:small-float-test$ raco make test.rkt stamourv@ahuntsic:small-float-test$ racket test.rkt #f stamourv@ahuntsic:small-float-test$ raco decompile test.rkt (begin (module test (#%apply-values |_print-values@(lib racket/private/modbeg.rkt)| '#f))) stamourv@ahuntsic:small-float-test$ This can make for pretty elusive bugs. This gets especially problematic when you add unsafe float operations to the mix, which can turn these issues into segfaults. The solution we picked was to support single-precision floats by default and to allow them in zos, which makes these discrepancies go away. I agree that having to handle single floats when reasoning about numbers complicates things, and it annoys me too. But I still think it's less problematic than what I describe above. This rationale does not explain why we have single precision floats at all, tho. Robby _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] What are single flonums good for?
On Wed, Sep 12, 2012 at 8:31 AM, Neil Toronto neil.toro...@gmail.com wrote: Compatibility with C code? Why not have the FFI convert them? Save space? I can see that. It won't help much if they're sent to math library functions, though. Those will convert them to flonums and usually box the converted values. I think these are big deals with respect to libraries that you deliver large float matrices to where you want to efficiently store a big f32vector rather than an f64vector. Examples of this include OpenGL where vector coordinates, colors, etc are typically floats and not doubles. Jay -- Jay McCarthy j...@cs.byu.edu Assistant Professor / Brigham Young University http://faculty.cs.byu.edu/~jay The glory of God is Intelligence - DC 93 _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] What are single flonums good for?
On Wed, Sep 12, 2012 at 3:47 PM, Matthew Flatt mfl...@cs.utah.edu wrote: At Wed, 12 Sep 2012 08:31:29 -0600, Neil Toronto wrote: Why do we have these things? I'm not sure this reason from 1996 is still relevant, but FWIW: Originally, there were drawing-related `float' computations in C code that I wanted to replicate exactly in Racket (ok, MzScheme). Eventually, I solved my specific consistency problem by changing the C code to use `double's; in other cases, someone might not be free to change the calculation. Would today's FFI make it easy (enough) to do those precise calculations? (Or maybe if we added a little library to help?) Robby _ Racket Developers list: http://lists.racket-lang.org/dev