Re: [Haskell-cafe] instance Enum Double considered not entirelygreat?
Quoth Richard O'Keefe o...@cs.otago.ac.nz, [ ... re Why would you write an upper bound of 0.3 on a list if you don't expect that to be included in the result? ] Because upper bounds are *UPPER BOUNDS* and are NOT as a rule included in the result. If you write [0,2..9] you - DO expect 0 in the result - DON'T expect 9 in the result - would be outraged if 10 were in the result. Pardon the questions from the gallery, but ... I can sure see that 0.3 shouldn't be included in the result by overshooting the limit (i.e., 0.30004), and the above expectations about [0,2..9] are obvious enough, but now I have to ask about [0,2..8] - would you not expect 8 in the result? Or is it not an upper bound? Donn ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] instance Enum Double considered not entirelygreat?
On Mon, 2011-09-26 at 19:54 -0700, Donn Cave wrote: Pardon the questions from the gallery, but ... I can sure see that 0.3 shouldn't be included in the result by overshooting the limit (i.e., 0.30004), and the above expectations about [0,2..9] are obvious enough, but now I have to ask about [0,2..8] - would you not expect 8 in the result? Or is it not an upper bound? Donn, [0, 2 .. 8] should be fine no matter the type, because integers of those sizes are all exactly representable in all of the types involved. The reason for the example with a step size of 0.1 is that 0.1 is actually an infinitely repeating number in base 2 (because the denominator has a prime factor of 5). So actually *none* of the exact real numbers 0.1, 0.2, or 0.3 are representable with floating point types. The corresponding literals actually refer to real numbers that are slightly off from those. Furthermore, because the step size is not *exactly* 0.1, when it's added repeatedly in the sequence, the result has some (very small) drift due to repeated rounding error... just enough that by the time you get in the vacinity of 0.3, the corresponding value in the sequence is actually *neither* the rational number 0.3, *nor* the floating point literal 0.3. Instead, it's one ulp larger than the floating point literal because of that drift. So there are two perspectives here. One is that we should think in terms of exact values of the type Float, which means we'd want to exclude it, because it's larger than the top end of the range. The other is that we should think of approximate values of real numbers, in which case it's best to pick the endpoint closest to the stated one, to correct for what's obviously unintended drift due to rounding. So that's what this is about: do we think of Float as an approximate real number type, or as an exact type with specific values. If the latter, then of course you exclude the value that's larger than the upper range. If the former, then using comparison operators like '' implies a proof obligation that the result of the computation remains stable (loosely speaking, the function continuous) at that boundary despite small rounding error in either direction. In that case, creating a language feature where, in the *normal* case of listing the last value you expect in the list, 0.3 (as an approximate real number) is excluded from this list just because of technicalities about the representation is an un-useful implementation, to say the least, and makes it far more difficult to satisfy that proof obligation. Personally, I see floating point values as approximate real numbers. Anything else in unrealistic: the *fact* of the matter is that no one is reasoning about ulps or exact rational values when they use Float and Double. In practice, even hardware implementations of some floating point functions have indeterminate results in the exact sense. Often, the guarantee provided by an FPU is that the result will be within one ulp of the correct answer, which means the exact value of the answer isn't even known! So, should we put all floating point calculations in the IO monad because they aren't pure functions? Or can we admit that people use floating point to approximate reals and accept the looser reasoning? -- Chris Smith ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] instance Enum Double considered not entirelygreat?
On 27/09/2011, at 3:54 PM, Donn Cave wrote: Quoth Richard O'Keefe o...@cs.otago.ac.nz, [ ... re Why would you write an upper bound of 0.3 on a list if you don't expect that to be included in the result? ] Because upper bounds are *UPPER BOUNDS* and are NOT as a rule included in the result. If you write [0,2..9] you - DO expect 0 in the result - DON'T expect 9 in the result - would be outraged if 10 were in the result. Pardon the questions from the gallery, but ... I can sure see that 0.3 shouldn't be included in the result by overshooting the limit (i.e., 0.30004), and the above expectations about [0,2..9] are obvious enough, but now I have to ask about [0,2..8] - would you not expect 8 in the result? Or is it not an upper bound? Quoting the Wikipedia: In mathematics, especially in order theory, an upper bound of a subset S of some partially ordered set (P, ≤) is an element of P which is greater than or equal to every element of S. ^^^ In the case of integers, where is the overshoot? I expect [L,M..U] to be the collection of all x such that L = x = U and x-L is n*(M-L) for some n, if M = L, or the collection of all x such that U = x = L and x-L is n*(M-L) for some n, if M = L. I don't say that's what Haskell promises. What I say is that that's what I _expect_. On the rare occasions when I've used REAL DO, getting close to the stated end point has been unimportant; not going outside the stated bounds critically important. In the case of [0,2..8], 0 = 8 = 8 and 8-0 is 4*(2-0), so 8 should be there. [0,1/10..3/10] :: [Ratio Int] includes 3/10 as it should; 3/10 = 3/10 so that's fine too. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] instance Enum Double considered not entirelygreat?
On 27/09/2011, at 4:55 PM, Chris Smith wrote: So there are two perspectives here. One is that we should think in terms of exact values of the type Float, which means we'd want to exclude it, because it's larger than the top end of the range. The other is that we should think of approximate values of real numbers, in which case it's best to pick the endpoint closest to the stated one, to correct for what's obviously unintended drift due to rounding. My perspective on this is neither of the above. My perspective is that if I say I want numbers in the range L..U, anything outside that range is wrong and could be catastrophically wrong. So that's what this is about: do we think of Float as an approximate real number type, or as an exact type with specific values. That may be the way you see it, but it's not the way I see it. The way I see it is that the *order* properties are more fundamental than the *arithmetic* properties, and that it is far less important to produce value which is approximately the end point (after all, [0,2..9] doesn't include 10) than it is to ensure that all the results are in the interval. And I would be *delighted* if Haskell's compare raised an exception if either argument were a NaN; that's what Real.compare does in SML. Floating point *operations* are approximate; floating point *numbers* are, according to the relevant standards (IEEE, LIA), precise. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] instance Enum Double considered not entirelygreat?
Quoth Chris Smith cdsm...@gmail.com, ... As for Enum, if someone were to want a type class to represent an enumeration of all the values of a type, then such a thing is reasonable to want. Maybe you can even reasonably wish it were called Enum. But it would be the *wrong* thing to use as a desugaring for list range notation. List ranges are very unlikely to be useful or even meaningful for most such enumerations (what is [ Red, Green .. LightPurple]?); and conversely, as we've seen in this thread, list ranges *are* useful in situations where they are not a suitable way of enumerating all values of a type. It isn't a life or death requirement, but I have dealt with enum values that are ordered and that could usefully be represented in a range. Even your example -- given a set of hues, it's sensible to order them following the spectrum, so [Cyan .. YellowGreen] might represent the hues in the set that are at all in the green range. I'm frankly using Enum as an equivalent to C enum, symbolic integers, where the range is more or less implicit as by default the integer values are [0..]. While I (vaguely) understand that Haskell's Enum is different, the C idiom should at least testify that there's some practical value in the notion of a range of Enum, or something like it. Though not with floating point values, of course. Donn ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe