logfloat-0.11.1: Log-domain floating point numbers




This module presents a type class for numbers which have representations for transfinite values. The idea originated from the IEEE-754 floating-point special values, used by Data.Number.LogFloat. However not all Fractional types necessarily support transfinite values. In particular, Ratio types including Rational do not have portable representations.

For the Glasgow compiler (GHC 6.8.2), GHC.Real defines 1%0 and 0%0 as representations for infinity and notANumber, but most operations on them will raise exceptions. If toRational is used on an infinite floating value, the result is a rational with a numerator sufficiently large that it will overflow when converted back to a Double. If used on NaN, the result would buggily convert back as negativeInfinity. For more discussion on why this approach is problematic, see:

Hugs (September 2006) stays closer to the haskell98 spec and offers no way of constructing those values, raising arithmetic overflow errors if attempted.



class PartialOrd a => Transfinite a whereSource

Many numbers are not Bounded yet, even though they can represent arbitrarily large values, they are not necessarily able to represent transfinite values such as infinity itself. This class is for types which are capable of representing such values. Notably, this class does not require the type to be Fractional nor Floating since integral types could also have representations for transfinite values. By popular demand the Num restriction has been lifted as well, due to complications of defining Show or Eq for some types.

In particular, this class extends the ordered projection to have a maximum value infinity and a minimum value negativeInfinity, as well as an exceptional value notANumber. All the natural laws regarding infinity and negativeInfinity should pertain. (Some of these are discussed below.)

Hugs (September 2006) has buggy Prelude definitions for isNaN and isInfinite on Float and Double. This module provides correct definitions, so long as Hugs.RealFloat is compiled correctly.


infinity :: aSource

A transfinite value which is greater than all finite values. Adding or subtracting any finite value is a no-op. As is multiplying by any non-zero positive value (including infinity), and dividing by any positive finite value. Also obeys the law negate infinity = negativeInfinity with all appropriate ramifications.

negativeInfinity :: aSource

A transfinite value which is less than all finite values. Obeys all the same laws as infinity with the appropriate changes for the sign difference.

notANumber :: aSource

An exceptional transfinite value for dealing with undefined results when manipulating infinite values. The following operations must return notANumber, where inf is any value which isInfinite:

  • inf + inf
  • inf - inf
  • inf * 0
  • 0 * inf
  • inf / inf
  • inf div inf
  • 0 / 0
  • 0 div 0

Additionally, any mathematical operations on notANumber must also return notANumber, and any equality or ordering comparison on notANumber must return False (violating the law of the excluded middle, often assumed but not required for Eq; thus, eq and ne are preferred over (==) and (/=)). Since it returns false for equality, there may be more than one machine representation of this value.

isInfinite :: a -> BoolSource

Return true for both infinity and negativeInfinity, false for all other values.

isNaN :: a -> BoolSource

Return true only for notANumber.

log :: (Floating a, Transfinite a) => a -> aSource

Since the normal log throws an error on zero, we have to redefine it in order for things to work right. Arguing from limits we can see that log 0 == negativeInfinity. Newer versions of GHC have this behavior already, but older versions and Hugs do not.

This function will raise an error when taking the log of negative numbers, rather than returning notANumber as the newer GHC implementation does. The reason being that typically this is a logical error, and notANumber allows the error to propegate silently.

In order to improve portability, the Transfinite class is required to indicate that the Floating type does in fact have a representation for negative infinity. Both native floating types (Double and Float) are supported. If you define your own instance of Transfinite, verify the above equation holds for your 0 and negativeInfinity. If it doesn't, then you should avoid importing our log and will probably want converters to handle the discrepancy.