Ticket #3070 (new bug)

Opened 4 years ago

Last modified 8 months ago

floor(0/0) should not be defined

Reported by: carette Owned by: squadette
Priority: lowest Milestone: 7.6.2
Component: Prelude Version: 6.10.1
Keywords: Cc: squadette@…, mihai.maruseac@…, anton.nik@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

floor(0/0) returns some giant negative integer - it should return NaN or undefined.

The bug appears to be in some implementation of 'properFraction' in the standard library.

[from Andrej Bauer, from Barak Pearlmutter (from ???)]

Change History

Changed 4 years ago by igloo

  • difficulty set to Unknown
  • milestone set to 6.12.1

Changed 4 years ago by crutcher

if decodeFloat (0.0/0.0) was changed to be undefined, then floor would be undefined.

Is there any support for this?

Changed 4 years ago by squadette

  • cc squadette@… added
  • owner set to squadette

Same thing happens with exponent, significand, scaleFloat, truncate, round, and ceiling.

I believe this patch should fix the problem:

 http://mynd.rinet.ru/~alexm/floor-3070.patch

1. Of course, we get some performance hit, with two ifs for every call to those functions, and the appearance of `undefined' (may it confuse the optimizer somehow?)

However, most of them seem to be not very performance oriented (except maybe for scaleFloat).

2. However, I believe that getting

significand (0/0) = -0.75

is not right, however fast it is.

3. withValidDouble could probably be made a monad of some sort?

4. properFraction should be reindented if this patch is applied. I did not make it to minimize changes.

5. maybe we need a special compiler flag which means "do not bother checking floats for validity, let the user handle details.

Thank you,

Changed 4 years ago by squadette

Well, it seems like this whole idea of correctness really contradicts some folks' expectations about speed:

 http://hackage.haskell.org/trac/ghc/ticket/2271

 http://hackage.haskell.org/trac/ghc/ticket/1434 ("This is really a problem for me when doing signal processing, since for writing to a common audio file format or listening to a signal data has to be converted from Double to Int16.")

 http://hackage.haskell.org/trac/ghc/ticket/2281

If we add even more specializations for primitive types, what will happen to correctness?

Haskell 98 seems to ignore the question of invalid floating point arguments, however recognizing the existence of NaNs? :)

Maybe we should just explicitly state somewhere that we sacrifice correctness to speed wrt Float <-> Int domain discrepancies.

Changed 4 years ago by squadette

or, the problem is purely of expectations.

People are used to the fact that evaluating 0/0 gives Division by zero runtime error.

Haskell however is so lazy that it does not bother exiting. Maybe a special command line argument is needed -- terminate on invalid FP operations. This could be useful for beginners, gentle introductions and such.

As for the day-to-day programming we just declare that Double cannot be converted to integral types anyway, so a) invalid operations give NaN, and b) all the functions will give you literal garbage when out of the target range (incl. NaN and Infinity).

AFAIU floating point developers are used to manage evaluation details carefully anyway.

Changed 3 years ago by igloo

  • failure set to None/Unknown
  • milestone changed from 6.12.1 to 6.12.2

See also #3676

Changed 3 years ago by carette

I don't think that exact patch is quite the way to go. Actually, I think that decodeFloat should be fixed to throw an exception when encountering NaN or infinities -- that's much more compatible with IEEE 754-2008. Also, there should probably be an isFinite :: Double -> Bool function, which would help guard against 'weird' floats with one check, not two.

Changed 3 years ago by simonpj

Jacques

We are stalled on this (and I think a couple of other fp-related tickets) because we lack the sophistication in numerical methods to develop the Right Answer, and no consensus has emerged. Would you, and/or others interested in numerical aspects of programming, like to figure out what we should do, make a proposal, and ultimately send us a patch? If it's just left for GHC HQ I fear that nothing may happen.

Simon

Changed 3 years ago by carette

I can do that - but not before mid-January.

Changed 3 years ago by igloo

  • milestone changed from 6.12.2 to 6.12.3

Changed 3 years ago by igloo

  • priority changed from normal to low
  • milestone changed from 6.12.3 to 6.14.1

Changed 2 years ago by igloo

  • milestone changed from 7.0.1 to 7.0.2

Changed 2 years ago by igloo

  • milestone changed from 7.0.2 to 7.2.1

Changed 20 months ago by igloo

  • milestone changed from 7.2.1 to 7.4.1

Changed 15 months ago by igloo

  • priority changed from low to lowest
  • milestone changed from 7.4.1 to 7.6.1

Changed 11 months ago by mihai.maruseac

  • cc mihai.maruseac@… added

Changed 11 months ago by simonmar

closed #5683 as a duplicate of this ticket (see more commentary there).

Changed 11 months ago by tristes_tigres

I've already wrote a few things over at closed as duplicate ticket, and would like to add the comment re the discussion above. It is quite correct that explicit checking for undefined operations like 0.0/0.0 is adversely affecting performance. What is worse, it affects the performance even the vast majority of operations, where NaN would not arise. And that is precisely why NaNs? were introduced - so that you don't have to check for invalid operations, but carry on computations to the conclusion, and if it blew up along the way, you'll see it, and see exaclty what parts were affected, since the result of every operation involving NaNs? is also NaN. The IEEE 754 standard authors even thought of providing a way to indicate the source of NaN - the special bit pattern indicating NaN has some "vacant" range, that an implementation may use to indicate thse source.

In general, it is a real pity that Haskell floating point design is so not thoghtful - it could have been a great tool for numerical analyst. For instance, the big thing that Haskell got wrong is that FP operations are not pure in the sense of being fully determined by their operands. IEEE754 provides for rounding direction flags, and those can be use to check the stability of numerical algorithms experimentally. But how to fit this into Haskell type system - that's pretty fundamental question.

Changed 11 months ago by simonpj

tristes_tigres, if you had some suggestions for how Haskell's FP design could be made more thoughtful, and more useful for numerical analysts, I think folk would definitely entertain them. People with sercious numerical-analysis expertise in the Haskell community are rare, so if you are such a person we'd gladly listen to you. Don't take the status quo as cast in stone.

Simon

Changed 11 months ago by lelf

  • cc anton.nik@… added

Changed 11 months ago by tristes_tigres

Re: simonpj

I am not sure that it is appropriate to call my limited knowledge of numerical analysis "serious expertise". But I do have some suggestions, for what it's worth, where Haskell handling of FP could be improved.

To start from the bug under discussion, I think that the suggestions to throw errors on round(0.0/0.0) are somewhat misguided and defeat the purpose of having NaNs? in the first place.

a) Inserting an "if" check will slow down (disrupt processor pipeline) every time round/ceil/floor/etc is called, even though NaNs? may occur in tiny fraction of the cases

b) Throwing exception on encountering NaN may lead to termination of the program, with many possibilities of bad things happening. It is possible that the result of invalid operation may not be actually needed, and occurred in some code left over in production by mistake (see the notorious case of Ariane 5 rocket blowing over FP exception, where precisely this happened). Clearly, this is undesirable outcome.

So how the bug can be corrected properly? By separating rounding and type conversion. It is IMHO a design error to conflate the two. Rounded floating point number is very typically used in further FP computations (I already gave an example - fdlibm sine/cosine functions). So there ought to be something like

ceilingf :: (Real a) => a -> a

floorf :: (Real a) => a -> a

truncatef :: (Real a) => => a -> a

roundf :: (Real a) => => a -> a

The existing round/floor/ceil may be implemented by adding type conversion step (if they should be implemented at all, which I am not sure about, other than for backward compatibility with existing software). It is this step where exception on NaN may be properly raised,as per IEEE 754 sec.7.1

There're possible further improvements to NaN handling beyond fixing this bug, too.

Changed 11 months ago by simonpj

Thanks. Your suggestions are indeed helpful. What this really needs is someone willing to take up the cudgels and propose, debate, and carry through the "NaN". Are there any numerical analysts who care about this stuff?

Changed 8 months ago by igloo

  • milestone changed from 7.6.1 to 7.6.2
Note: See TracTickets for help on using tickets.