Proposal: New Eq and Ord instances for Double and Float

Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order. Discussion period: Two weeks Rationale: The Eq and Ord instances for Double and Float currently follow the IEEE specification in that * NaN == x = False * NaN /= x = True * NaN < x = False * NaN > x = False * NaN <= x = False * NaN >= x = False This has several undesirable consequences: - (==) does not define an equivalence relation on those types - (<) does not define a total order on those types (not even a partial order) - compare always returns GT if (at least) one argument is a NaN As a result, various algorithms and data structures are broken in the presence of NaNs: Prelude Data.List Data.Set> let nan :: Double; nan = 0/0 Prelude Data.List Data.Set> max nan 1 NaN Prelude Data.List Data.Set> max 1 nan 1.0 Prelude Data.List Data.Set> minimum [0,1,nan,2,nan,3,4,nan,5,6] 5.0 Prelude Data.List Data.Set> sort [0,1,nan,2,nan,3,4,nan,5,6] [0.0,1.0,3.0,5.0,6.0,NaN,4.0,NaN,2.0,NaN] Prelude Data.List Data.Set> let st = fromList [0,1,nan,2,nan,3,4,nan,5,6] Prelude Data.List Data.Set> Prelude.filter (`member` st) [0 .. 10] [0.0,1.0,2.0,5.0,6.0] Prelude Data.List Data.Set> Prelude.filter (`member` (Data.Set.filter (not . isNaN) st)) [0 .. 10] [0.0,1.0,2.0,3.0,4.0,5.0,6.0] ===================== I therefore propose to change the Eq and Ord instances for Double and Float to conform with the behaviour expected from such instances in Haskell, and to make the comparisons with IEEE semantics available from the RealFloat class. In more detail: * Eq: The Eq instance for Double and Float shall be changed so that NaN == NaN = True NaN /= NaN = False Since there are many bit patterns which are NaN values, there remains the question whether all NaNs shall be considered equal or whether only NaNs with identical bit patterns should be considered equal. I lean towards treating all NaNs equal to each other. A different edge-case is negative zero. At the moment, we have 0.0 == (-0.0) = True as mandated by IEEE. The question is whether that should be kept or we want to disinguish between 0.0 and -0.0. Because of recip 0.0 = Infinity recip (-0.0) = -Infinity I lean towards distinguishing. * Ord: The Ord instance shall be modified so that - if currently x < y holds, that shall remain so - NaN shall (arbitrarily, in analogy to Maybe's Ord instance[s]) be considered smaller than all non-NaN values. If Eq should distinguish different NaNs, the Ord instance should do the same. If Eq should distinguish 0.0 and -0.0, so should the Ord instance. * IEEE comparisons: These shall be made available from the RealFloat class (where applicable, if the hardware doesn't provide them, default them to the Ord methods). Two reasons: first their semantics might be needed in some programmes; second, as hardware operations, they're fast, in number-crunching programmes where NaNs can't occur, that can make a significant difference. These operators should be visually similar to the Eq and Ord operators but not too similar. The ML languages - afaik - mark operators on floating point values by appending a dot, (+.), (<.) etc. That seems too easy to overlook to me, by enclosing the operator in dots, (.==.), (.<.) the opportunity to spot the difference is doubled without being too heavy notation. On the other hand, we already use the dot for other things, so using dots as the distinguishing means may be less that optimal. Suggestions welcome.

I prefer to stick with IEEE which is well understood and standardized.

On Sun, Sep 25, 2011 at 10:53 PM, Daniel Fischer < daniel.is.fischer@googlemail.com> wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I am strongly against this proposal, as it is in an area that is particularly poorly understood by most programmers (even experts). While the current behaviour may be undesirable from some points of view, at least it is consistent with most other programming languages - and this is one of the few areas where I think that's important.

Is it? our Ord class already violates the IEEE spec (and can't help it), so
we might as well violate it and be mathematically useful.
On Mon, Sep 26, 2011 at 1:05 AM, Bryan O'Sullivan
On Sun, Sep 25, 2011 at 10:53 PM, Daniel Fischer < daniel.is.fischer@googlemail.com> wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I am strongly against this proposal, as it is in an area that is particularly poorly understood by most programmers (even experts). While the current behaviour may be undesirable from some points of view, at least it is consistent with most other programming languages - and this is one of the few areas where I think that's important.
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

Oh actually, (I think) I was wrong about us violating the laws (if you
ignore the compare method's implied meaning). It does, however, violate any
kinds of laws we might have for Ord, where compare should agree with the
Bool comparison operators, and e.g. min/max should be commutative.
Basically, I don't think we should be held back by shitty conventions that
other languages (or standards bodies) introduced. It's not like you're
really *supposed* to be comparing vanilla Floats/Doubles, so most people try
to avoid it anyway. The scheme in this proposal would remove one of the
reasons it's usually nonsensical to compare them directly, if anything.
I'm in favor, but not strongly so. I just think it's an ugly wart on the
language that doesn't really buy us much beyond compliance with a flawed
standard.
On Mon, Sep 26, 2011 at 1:10 AM, Daniel Peebles
Is it? our Ord class already violates the IEEE spec (and can't help it), so we might as well violate it and be mathematically useful.
On Mon, Sep 26, 2011 at 1:05 AM, Bryan O'Sullivan
wrote: On Sun, Sep 25, 2011 at 10:53 PM, Daniel Fischer < daniel.is.fischer@googlemail.com> wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I am strongly against this proposal, as it is in an area that is particularly poorly understood by most programmers (even experts). While the current behaviour may be undesirable from some points of view, at least it is consistent with most other programming languages - and this is one of the few areas where I think that's important.
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

I too am against this proposal.
I'd be for it for consistency if perhaps we were writing a language from scratch, because it has the transitive effect that one can't use pointer equality to short circuit structural equality checks anywhere the types involved are parametric or contain Floats, but it would subtly and silently break a good deal of existing code.
-Edward
Sent from my iPad
On Sep 26, 2011, at 1:05 AM, "Bryan O'Sullivan"
On Sun, Sep 25, 2011 at 10:53 PM, Daniel Fischer
wrote: Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order. I am strongly against this proposal, as it is in an area that is particularly poorly understood by most programmers (even experts). While the current behaviour may be undesirable from some points of view, at least it is consistent with most other programming languages - and this is one of the few areas where I think that's important. _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

How much correct code relies on the output of comparisons with NaN? The only
thing I can think of is when you're trying to reimplement isNaN.
On Mon, Sep 26, 2011 at 1:23 AM, Edward Kmett
I too am against this proposal.
I'd be for it for consistency if perhaps we were writing a language from scratch, because it has the transitive effect that one can't use pointer equality to short circuit structural equality checks anywhere the types involved are parametric or contain Floats, but it would subtly and silently break a good deal of existing code.
-Edward
Sent from my iPad
On Sep 26, 2011, at 1:05 AM, "Bryan O'Sullivan"
wrote: On Sun, Sep 25, 2011 at 10:53 PM, Daniel Fischer <
daniel.is.fischer@googlemail.com> wrote: Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I am strongly against this proposal, as it is in an area that is particularly poorly understood by most programmers (even experts). While the current behaviour may be undesirable from some points of view, at least it is consistent with most other programming languages - and this is one of the few areas where I think that's important.
_______________________________________________
Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

Bryan O'Sullivan writes:
I am strongly against this proposal, as it is in an area that is particularly poorly understood by most programmers (even experts).
Whilst Johan Tibell writes:
I prefer to stick with IEEE which is well understood and standardized.
So which is it? Are these standards well-understood or not? I would argue that the unexpected and unintuitive behaviour of IEEE floats trip up many programmers. (But that does not necessarily mean that the proposal is going to help.) For myself, I would prefer NaNs to compare equal. But if we are suggesting a departure from IEEE, then maybe an even better solution would be to make NaNs exceptions instead of values? Regards, Malcolm

On 9/26/11 1:05 AM, Bryan O'Sullivan wrote:
On Sun, Sep 25, 2011 at 10:53 PM, Daniel Fischer< daniel.is.fischer@googlemail.com> wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I am strongly against this proposal, as it is in an area that is particularly poorly understood by most programmers (even experts). While the current behaviour may be undesirable from some points of view, at least it is consistent with most other programming languages - and this is one of the few areas where I think that's important.
I am also opposed to this proposal. I agree that the current situation is problematic, however I do not see this proposal as an improvement. Actual improvements would be to: (a) distinguish classes for semantic ordering vs arbitrary ordering, where the latter is used by Data.Map and friends, whereas the former is used whenever we expect the results to be meaningful; or (b) distinguish classes for partial orders vs total orders, either as is done in the logfloat package, by flattening the (Maybe Ordering) representation if people find that preferable, or by figuring out some better theory/combinators for partial orders. In either case, Double/Float will not have instances for Ord because these types do not have a semantic total ordering. Moreover, the current proposal grossly violates the principle of least surprise (for new users) and is guaranteed to introduce subtle and obscure bugs (for old users) since we cannot detect where the old behavior is being relied upon, either explicitly or implicitly. -- Live well, ~wren

Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
Some implementations of Haskell *already* provide a total ordering for the Float type, and this is perfectly allowed within the definition of the 2010 Language Report. To quote Section 6.4: "Float is implementation-defined; it is desirable that this type be at least equal in range and precision to the IEEE single-precision type. [...] The results of exceptional conditions (such as overflow or underflow) on the fixed-precision numeric types are undefined; an implementation may choose error (⊥, semantically), a truncated value, or a special value such as infinity, indefinite, etc." If any program intends to rely on the IEEE behaviour for exceptional conditions, then for correctness, it really ought to use the RealFloat class method "isIEEE" to be certain that the compiler implementation actually supports that. Regards, Malcolm

Daniel Fischer wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I'm strongly against this, for the reasons that have already been mentioned. and because there a good reasons for why the IEEE semantics is the way it is. Also, performance is going to suffer horribly.
As a result, various algorithms and data structures are broken in the presence of NaNs:
Prelude Data.List Data.Set> let nan :: Double; nan = 0/0 Prelude Data.List Data.Set> max nan 1 NaN Prelude Data.List Data.Set> max 1 nan 1.0 Prelude Data.List Data.Set> minimum [0,1,nan,2,nan,3,4,nan,5,6] 5.0 Prelude Data.List Data.Set> sort [0,1,nan,2,nan,3,4,nan,5,6] [0.0,1.0,3.0,5.0,6.0,NaN,4.0,NaN,2.0,NaN] Prelude Data.List Data.Set> let st = fromList [0,1,nan,2,nan,3,4,nan,5,6] Prelude Data.List Data.Set> Prelude.filter (`member` st) [0 .. 10] [0.0,1.0,2.0,5.0,6.0] Prelude Data.List Data.Set> Prelude.filter (`member` (Data.Set.filter (not . isNaN) st)) [0 .. 10] [0.0,1.0,2.0,3.0,4.0,5.0,6.0] =====================
I would say it's a precondition of min, max and friends that none of the numbers involved are NaNs. In fact, one could argue that the Eq and Ord instances have the same precondition and hence define a total order, but can only handle a subset of the Double/Float values. IMO, anybody who stores Doubles in a Set either really knows what he's doing (in which case NaNs shouldn't be a problem) or is just doing it wrong. In general, these examples just look broken to me regardless of how Eq and Ord are defined. If I saw something like this happen in real code I'd assume it's a bug. Roman

On 26 September 2011 10:53, Roman Leshchinskiy
Daniel Fischer wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I'm strongly against this, for the reasons that have already been mentioned. and because there a good reasons for why the IEEE semantics is the way it is. Also, performance is going to suffer horribly.
Just in case we need any more votes on this, I agree with Bryan and Roman. Float and Double are specified by the IEEE and we should just do whatever crazy thing it is they specify. Going further and supporting more of the IEEE spec would be interesting (like signalling NANs reflected as exceptions). Duncan

On Mon, 26 Sep 2011, Duncan Coutts wrote:
Just in case we need any more votes on this, I agree with Bryan and Roman. Float and Double are specified by the IEEE and we should just do whatever crazy thing it is they specify.
As far as I understand the Haskell 98 report, there is no need that Float and Double are in IEEE format or behave like IEEE floating point numbers. Why not having IEEESingle and IEEEDouble as types for IEEE floating point numbers? The problems with the Set type are also due to the fact, that Set does not really need an Ord instance in the sense of an ordering with respect to magnitude. Set would also work with an ordering like NaN < -Inf < -0 < 0 < Inf. I think IEEE numbers only yield one more argument to use a type class other than Ord for elements of Sets.

On Mon, Sep 26, 2011 at 6:53 AM, Roman Leshchinskiy
IMO, anybody who stores Doubles in a Set either really knows what he's doing (in which case NaNs shouldn't be a problem) or is just doing it wrong.
In general, these examples just look broken to me regardless of how Eq and Ord are defined. If I saw something like this happen in real code I'd assume it's a bug.
At the very least there should be more documentation about this problem (I don't know where). Even for those who know about the IEEE standard, it's not obvious at all that these functions won't work. The more complex the example (max → sort → Data.Set), the less straightforward the problem becomes. Instead of this proposal, it may also be interesting to propose something like newtype Total a = Total a type TotalDouble = Total Double type TotalFloat = Total Float which provides meaningful Eq and Ord instances for algorithms that don't care about what a NaN is. Cheers, -- Felipe.

Roman Leshchinskiy writes:
Daniel Fischer wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I'm strongly against this, for the reasons that have already been mentioned. and because there a good reasons for why the IEEE semantics is the way it is.
But compare cannot implement the IEEE semantics and be total, because the Ordering type cannot represent "unordered". Something has to give. The nearest compare can do is to throw an exception if an argument is NaN (with compatible behaviour from the comparison operators). At least that would not be silent or subtle breakage.

Paterson, Ross wrote:
Roman Leshchinskiy writes:
Daniel Fischer wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I'm strongly against this, for the reasons that have already been mentioned. and because there a good reasons for why the IEEE semantics is the way it is.
But compare cannot implement the IEEE semantics and be total, because the Ordering type cannot represent "unordered". Something has to give. The nearest compare can do is to throw an exception if an argument is NaN (with compatible behaviour from the comparison operators).
Why can't compare just specify that its results are undefined when applied to NaN? As a matter of fact, the Haskell report already does this. It explicitly says that the results of evaluating expressions like 0/0 are undefined which means that applying compare to them produces undefined results as well.
At least that would not be silent or subtle breakage.
IMO, if we really want to avoid silent breakage we shouldn't have silent NaNs by default. That is, evaluating 0/0 should throw an exception. Unless I'm mistaken, this can be implemented by simply setting the appropriate processor flag. Personally, I would be much more open to a proposal to make this the default as long as there is no runtime cost and silent NaNs can be turned back on somehow if a program needs them. Roman

A proposal that made signaling NaN the default is something I could get behind.
Sent from my iPad
On Sep 26, 2011, at 9:57 AM, "Roman Leshchinskiy"
Paterson, Ross wrote:
Roman Leshchinskiy writes:
Daniel Fischer wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I'm strongly against this, for the reasons that have already been mentioned. and because there a good reasons for why the IEEE semantics is the way it is.
But compare cannot implement the IEEE semantics and be total, because the Ordering type cannot represent "unordered". Something has to give. The nearest compare can do is to throw an exception if an argument is NaN (with compatible behaviour from the comparison operators).
Why can't compare just specify that its results are undefined when applied to NaN? As a matter of fact, the Haskell report already does this. It explicitly says that the results of evaluating expressions like 0/0 are undefined which means that applying compare to them produces undefined results as well.
At least that would not be silent or subtle breakage.
IMO, if we really want to avoid silent breakage we shouldn't have silent NaNs by default. That is, evaluating 0/0 should throw an exception. Unless I'm mistaken, this can be implemented by simply setting the appropriate processor flag. Personally, I would be much more open to a proposal to make this the default as long as there is no runtime cost and silent NaNs can be turned back on somehow if a program needs them.
Roman
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

Same. I'd still hate the instances, but my practical concerns would be
mostly covered by it.
On Mon, Sep 26, 2011 at 10:14 AM, Edward Kmett
A proposal that made signaling NaN the default is something I could get behind.
Sent from my iPad
On Sep 26, 2011, at 9:57 AM, "Roman Leshchinskiy"
wrote: Paterson, Ross wrote:
Roman Leshchinskiy writes:
Daniel Fischer wrote:
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
I'm strongly against this, for the reasons that have already been mentioned. and because there a good reasons for why the IEEE semantics is the way it is.
But compare cannot implement the IEEE semantics and be total, because the Ordering type cannot represent "unordered". Something has to give. The nearest compare can do is to throw an exception if an argument is NaN (with compatible behaviour from the comparison operators).
Why can't compare just specify that its results are undefined when applied to NaN? As a matter of fact, the Haskell report already does this. It explicitly says that the results of evaluating expressions like 0/0 are undefined which means that applying compare to them produces undefined results as well.
At least that would not be silent or subtle breakage.
IMO, if we really want to avoid silent breakage we shouldn't have silent NaNs by default. That is, evaluating 0/0 should throw an exception. Unless I'm mistaken, this can be implemented by simply setting the appropriate processor flag. Personally, I would be much more open to a proposal to make this the default as long as there is no runtime cost and silent NaNs can be turned back on somehow if a program needs them.
Roman
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

Daniel> Same. I'd still hate the instances, but my practical
Daniel> concerns would be mostly covered by it.
Daniel> On Mon, Sep 26, 2011 at 10:14 AM, Edward Kmett

Colin Paul Adams wrote:
A quick google for signalling NaNs seems to suggest that on the x86 architecture, you have to set a flag to raise an exception when encountering signalling NaNs. Otherwise they are just treated as quiet NaNs. But you have to create the signalling NaNs manually. They are not created as a result of arithmetic operations.
IIRC, with the appropriate flags set, arithmetic operations throw exceptions instead of creating NaNs. You can't really create a signalling NaN in a register because as soon as you do, you get a signal. Roman

On 27/09/2011 10:02, Roman Leshchinskiy wrote:
Colin Paul Adams wrote:
A quick google for signalling NaNs seems to suggest that on the x86 architecture, you have to set a flag to raise an exception when encountering signalling NaNs. Otherwise they are just treated as quiet NaNs. But you have to create the signalling NaNs manually. They are not created as a result of arithmetic operations.
IIRC, with the appropriate flags set, arithmetic operations throw exceptions instead of creating NaNs. You can't really create a signalling NaN in a register because as soon as you do, you get a signal.
But they don't throw a Haskell exception, they throw a processor exception which kills your whole program. If we want a Haskell exception to result from 0/0, we have to insert extra checking code, which I'm sure you won't like :-) Cheers, Simon

Simon Marlow wrote:
On 27/09/2011 10:02, Roman Leshchinskiy wrote:
Colin Paul Adams wrote:
A quick google for signalling NaNs seems to suggest that on the x86 architecture, you have to set a flag to raise an exception when encountering signalling NaNs. Otherwise they are just treated as quiet NaNs. But you have to create the signalling NaNs manually. They are not created as a result of arithmetic operations.
IIRC, with the appropriate flags set, arithmetic operations throw exceptions instead of creating NaNs. You can't really create a signalling NaN in a register because as soon as you do, you get a signal.
But they don't throw a Haskell exception, they throw a processor exception which kills your whole program. If we want a Haskell exception to result from 0/0, we have to insert extra checking code, which I'm sure you won't like :-)
I would, of course, expect the RTS to convert the processor exception to a Haskell exception! Roman

On 10/11/2011 16:28, Roman Leshchinskiy wrote:
Simon Marlow wrote:
On 27/09/2011 10:02, Roman Leshchinskiy wrote:
Colin Paul Adams wrote:
A quick google for signalling NaNs seems to suggest that on the x86 architecture, you have to set a flag to raise an exception when encountering signalling NaNs. Otherwise they are just treated as quiet NaNs. But you have to create the signalling NaNs manually. They are not created as a result of arithmetic operations.
IIRC, with the appropriate flags set, arithmetic operations throw exceptions instead of creating NaNs. You can't really create a signalling NaN in a register because as soon as you do, you get a signal.
But they don't throw a Haskell exception, they throw a processor exception which kills your whole program. If we want a Haskell exception to result from 0/0, we have to insert extra checking code, which I'm sure you won't like :-)
I would, of course, expect the RTS to convert the processor exception to a Haskell exception!
You have high expectations :-) I don't think it's possible to do that without some very low-level platform-specific and processor-specific hacking, which is why for example we have the current software test for divide-by-zero. You basically get a signal and have to grovel around in the thread's registers and stack to recover the situation, and the exception could be thrown from *anywhere*. Cheers, Simon

Simon Marlow wrote:
On 10/11/2011 16:28, Roman Leshchinskiy wrote:
Simon Marlow wrote:
On 27/09/2011 10:02, Roman Leshchinskiy wrote:
Colin Paul Adams wrote:
A quick google for signalling NaNs seems to suggest that on the x86 architecture, you have to set a flag to raise an exception when encountering signalling NaNs. Otherwise they are just treated as quiet NaNs. But you have to create the signalling NaNs manually. They are not created as a result of arithmetic operations.
IIRC, with the appropriate flags set, arithmetic operations throw exceptions instead of creating NaNs. You can't really create a signalling NaN in a register because as soon as you do, you get a signal.
But they don't throw a Haskell exception, they throw a processor exception which kills your whole program. If we want a Haskell exception to result from 0/0, we have to insert extra checking code, which I'm sure you won't like :-)
I would, of course, expect the RTS to convert the processor exception to a Haskell exception!
You have high expectations :-) I don't think it's possible to do that without some very low-level platform-specific and processor-specific hacking, which is why for example we have the current software test for divide-by-zero. You basically get a signal and have to grovel around in the thread's registers and stack to recover the situation, and the exception could be thrown from *anywhere*.
Oh, I never said it would be easy :-) But this definitely seems like the right thing to do to me. In the context of this thread, however, it would be perfectly acceptable if NaNs just aborted the program. The original problem was that they mess up things. It is implementation-defined what happens if a computation wants to create a NaN. We could simply say that the program is aborted by default, with a way to turn off this behaviour and just create a NaN. Raising a Haskell exception would certainly be very nice but not essential for this particular problem. Roman

On 10/11/11 18:03, Roman Leshchinskiy wrote:
Oh, I never said it would be easy :-) But this definitely seems like the right thing to do to me.
In the context of this thread, however, it would be perfectly acceptable if NaNs just aborted the program. The original problem was that they mess up things. It is implementation-defined what happens if a computation wants to create a NaN. We could simply say that the program is aborted by default, with a way to turn off this behaviour and just create a NaN. Raising a Haskell exception would certainly be very nice but not essential for this particular problem.
That seems dangerous. This is what happens now if I use unchecked div for example: $ ghci Prelude> GHC.Base.divInt 1 0 Floating point exception $ It exits ghci immediately. Having "0/0" crash would make trusting 'safe' programs much harder. For example, I could no longer make an IRC bot for evaluating numeric expressions, and expect it to keep running. Twan

Twan van Laarhoven wrote:
On 10/11/11 18:03, Roman Leshchinskiy wrote:
Oh, I never said it would be easy :-) But this definitely seems like the right thing to do to me.
In the context of this thread, however, it would be perfectly acceptable if NaNs just aborted the program. The original problem was that they mess up things. It is implementation-defined what happens if a computation wants to create a NaN. We could simply say that the program is aborted by default, with a way to turn off this behaviour and just create a NaN. Raising a Haskell exception would certainly be very nice but not essential for this particular problem.
That seems dangerous. This is what happens now if I use unchecked div for example:
$ ghci Prelude> GHC.Base.divInt 1 0 Floating point exception $
One could argue that this is a bug in ghci - perhaps it shouldn't abort if evaluating an expression causes an error.
It exits ghci immediately. Having "0/0" crash would make trusting 'safe' programs much harder. For example, I could no longer make an IRC bot for evaluating numeric expressions, and expect it to keep running.
You could by explicitly enabling silent NaNs and handling them specially. The original problem was that we currently silently generates NaNs which have surprising semantics if you aren't specifically dealing with them. This problem is solved by having NaNs abort the program and having a way to recover the current behaviour. Again, having a Haskell exception would be vastly preferable but as a short-term solution, aborting the program seems acceptable to me. Roman

On 11/11/2011 14:02, Roman Leshchinskiy wrote:
Twan van Laarhoven wrote:
On 10/11/11 18:03, Roman Leshchinskiy wrote:
Oh, I never said it would be easy :-) But this definitely seems like the right thing to do to me.
In the context of this thread, however, it would be perfectly acceptable if NaNs just aborted the program. The original problem was that they mess up things. It is implementation-defined what happens if a computation wants to create a NaN. We could simply say that the program is aborted by default, with a way to turn off this behaviour and just create a NaN. Raising a Haskell exception would certainly be very nice but not essential for this particular problem.
That seems dangerous. This is what happens now if I use unchecked div for example:
$ ghci Prelude> GHC.Base.divInt 1 0 Floating point exception $
One could argue that this is a bug in ghci - perhaps it shouldn't abort if evaluating an expression causes an error.
GHC.Base.divInt is an unsafe function, in the same way that unsafeCoerce is: you can crash the program by using it in the wrong way. Maybe it should be called unsafeDivInt, but this isn't a user-visible API, so we don't really care.
It exits ghci immediately. Having "0/0" crash would make trusting 'safe' programs much harder. For example, I could no longer make an IRC bot for evaluating numeric expressions, and expect it to keep running.
You could by explicitly enabling silent NaNs and handling them specially. The original problem was that we currently silently generates NaNs which have surprising semantics if you aren't specifically dealing with them. This problem is solved by having NaNs abort the program and having a way to recover the current behaviour.
Again, having a Haskell exception would be vastly preferable but as a short-term solution, aborting the program seems acceptable to me.
I'm surprised to hear you say that - aborting the program is *never* acceptable behaviour as far as I'm concerned, unless the API is explicitly labelled "unsafe" and you use it in an unsafe way. Even the rare places in Haskell where behaviour is explicitly undefined never abort the program in GHC. It would be a shame if any module using division were not allowed to be classed as "safe" by SafeHaskell. Cheers, Simon

On November 10, 2011 11:48:33 Simon Marlow wrote:
On 10/11/2011 16:28, Roman Leshchinskiy wrote:
I would, of course, expect the RTS to convert the processor exception to a Haskell exception!
You have high expectations :-) I don't think it's possible to do that without some very low-level platform-specific and processor-specific hacking, which is why for example we have the current software test for divide-by-zero. You basically get a signal and have to grovel around in the thread's registers and stack to recover the situation, and the exception could be thrown from *anywhere*.
Historically some processors have also made these exceptions imprecise as faster execution can be produced by not having the pipeline be constrained by the requirement to roll earlier stage effects back if an exception is raised. http://books.google.ca/books?id=GumAeql5KPkC&pg=SA4-PA69&lpg=SA4- PA69&dq=alpha+imprecise+traps While software barriers can be inserted to make it possible to pin exceptions down to greater degrees (see -mtrap-precision), it costs you performance. http://gcc.gnu.org/onlinedocs/gcc/DEC-Alpha-Options.html Cheers! -Tyson

On Mon, Sep 26, 2011 at 5:53 AM, Daniel Fischer
Proposal: Provide Double and Float with Eq and Ord instances that introduce a total order.
Discussion period: Two weeks
Rationale:
The Eq and Ord instances for Double and Float currently follow the IEEE specification in that * NaN == x = False * NaN /= x = True * NaN < x = False * NaN > x = False * NaN <= x = False * NaN >= x = False
This has several undesirable consequences: - (==) does not define an equivalence relation on those types - (<) does not define a total order on those types (not even a partial order) - compare always returns GT if (at least) one argument is a NaN
As a result, various algorithms and data structures are broken in the presence of NaNs:
Prelude Data.List Data.Set> let nan :: Double; nan = 0/0 Prelude Data.List Data.Set> max nan 1 NaN Prelude Data.List Data.Set> max 1 nan 1.0 Prelude Data.List Data.Set> minimum [0,1,nan,2,nan,3,4,nan,5,6] 5.0 Prelude Data.List Data.Set> sort [0,1,nan,2,nan,3,4,nan,5,6] [0.0,1.0,3.0,5.0,6.0,NaN,4.0,NaN,2.0,NaN] Prelude Data.List Data.Set> let st = fromList [0,1,nan,2,nan,3,4,nan,5,6] Prelude Data.List Data.Set> Prelude.filter (`member` st) [0 .. 10] [0.0,1.0,2.0,5.0,6.0] Prelude Data.List Data.Set> Prelude.filter (`member` (Data.Set.filter (not . isNaN) st)) [0 .. 10] [0.0,1.0,2.0,3.0,4.0,5.0,6.0] =====================
I therefore propose to change the Eq and Ord instances for Double and Float to conform with the behaviour expected from such instances in Haskell, and to make the comparisons with IEEE semantics available from the RealFloat class.
In more detail:
* Eq:
The Eq instance for Double and Float shall be changed so that NaN == NaN = True NaN /= NaN = False
Since there are many bit patterns which are NaN values, there remains the question whether all NaNs shall be considered equal or whether only NaNs with identical bit patterns should be considered equal.
I lean towards treating all NaNs equal to each other.
I just want to point out that (everyone probably knows this, but it wasn't mentioned explicitly), as I understand it, conceptually a NaN value of a floating-point type has a different meaning from (say) a Nothing value of a Maybe type. Nothing usually means Nothing. NaN means "anything else": an undefined, invalid, unrepresentable, *or* missing number. The operation that produced it might've had a well-defined value in theory, but the type couldn't represent it. So it's so-to-speak an existential value: it has a value, you just can't know what it is. In the same way that existential types don't unify with any other types, even other existential types, NaNs don't compare equal with any other value, even other NaNs. NaN /= NaN because sqrt(-2) /= sqrt(-3). Notwithstanding the above, it might be useful practically to treat a NaN as analogous to a Nothing anyways (as per your examples), so this is neither a vote for nor against.
A different edge-case is negative zero. At the moment, we have 0.0 == (-0.0) = True as mandated by IEEE. The question is whether that should be kept or we want to disinguish between 0.0 and -0.0.
Because of
recip 0.0 = Infinity recip (-0.0) = -Infinity
I lean towards distinguishing.
* Ord:
The Ord instance shall be modified so that - if currently x < y holds, that shall remain so - NaN shall (arbitrarily, in analogy to Maybe's Ord instance[s]) be considered smaller than all non-NaN values.
If Eq should distinguish different NaNs, the Ord instance should do the same. If Eq should distinguish 0.0 and -0.0, so should the Ord instance.
* IEEE comparisons:
These shall be made available from the RealFloat class (where applicable, if the hardware doesn't provide them, default them to the Ord methods).
Two reasons: first their semantics might be needed in some programmes; second, as hardware operations, they're fast, in number-crunching programmes where NaNs can't occur, that can make a significant difference.
These operators should be visually similar to the Eq and Ord operators but not too similar. The ML languages - afaik - mark operators on floating point values by appending a dot, (+.), (<.) etc. That seems too easy to overlook to me, by enclosing the operator in dots, (.==.), (.<.) the opportunity to spot the difference is doubled without being too heavy notation. On the other hand, we already use the dot for other things, so using dots as the distinguishing means may be less that optimal. Suggestions welcome.
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
-- Work is punishment for failing to procrastinate effectively.
participants (17)
-
Bryan O'Sullivan
-
Colin Paul Adams
-
Daniel Fischer
-
Daniel Peebles
-
Duncan Coutts
-
Edward Kmett
-
Felipe Almeida Lessa
-
Gábor Lehel
-
Henning Thielemann
-
Johan Tibell
-
Malcolm Wallace
-
Paterson, Ross
-
Roman Leshchinskiy
-
Simon Marlow
-
Twan van Laarhoven
-
Tyson Whitehead
-
wren ng thornton