Well, the problem is not only for infinity (Overflow).
It is also true for underflow. (2/(11 raisedTo: 400)) = 0.0. "is true" (2/(11 raisedTo: 400)) = 0. "is false"
And in fact, the equality transitivity problem can show up for any fraction not exactly representable in IEEE Floating point, that is most Fractions.
Since Fraction set is dense in set of real numbers, i can find an infinite number of Fractions lying between two Float. Since IEEE floating point arithmetic only have a finite number of possible values, then for each floating point value, there exist an infinite number of fractions that will be equal to this float (in current implementation, not mathematically), but the fraction will not equal to each other, thanks to exact arithmetic.
Example: | a b | a := (16rFFFFFFFFFFFFF11 / 16r1000000000000000). b := (16rFFFFFFFFFFFFF10 / 16r1000000000000000). c := a asFloat. {a = b. a = c. b = c.}. "is {false . true . true}"
Knowing this, i'am not sure that ((1/3) asFloat = (1/3)) should answer true. Maybe that should be only the case for exact representation like 1/2 1/4 3/4 etc...
But in this case, we also have to redefine coercion to coercing to Fraction instead of Float, because Fraction are more general (yes, Float is a subset of Fraction).
If we do not coerce to exact arithmetic, we will have ((1/3) asFloat - (1/3)) isZero, and still we might be caught by some form of equality problem...
Thus are we ready to exchange our fast floating point algorithm for slower Fraction, with a huge number of useless digits, each time somebody introduce a Fraction. Are we really sure we vote for this perfect system ? I am not sure at all.
And that is also the case between floating points and integers. Sure other dialects also have the equality problem, maybe not for Infinity, but for less trivial inexact arithmetic case.
All is coming from problem of these languages calling floating point numbers real. Real is more general than fraction, ah yes, we have been foolished by this one.
What should we do ?
What is your opinion ?
________________________________________________________________________ iFRANCE, exprimez-vous ! http://web.ifrance.com
Hello ncellier,
Tuesday, March 28, 2006, 7:25:46 AM, you wrote:
nic> [number hashing is broken] nic> What is your opinion ?
IMHO... sometimes it may be convenient to have 2 = 2.0, but since they represent different intentions, I would not expect that to happen. Why should a perfectly specified integer be equal to, possibly, the result of carrying calculations without infinite precision? What would it mean if theGreatDoublePrecisionResult = 2? Wouldn't it be more interesting (and precise) to ask laysWithin: epsilon from: 2?
In other words, comparing constants makes it look much simpler than what it really is. Reading something like anInteger = 2.0 would be, from my point of view, highly questionable because it is an assertion that an approximation has an *exact* value. Nonsense.
From a more pragmatic point of view, there is also the issue of 2 =
2.0, but things like
(1 bitShift: 1000) - 1
cannot be equal to any floating point number supported by common hardware. Thus, exploiting anInteger = aFloat is intention obscuring by definition since it may or may not work depending on the integer. Again, highly questionable.
In short: floating point numbers *may* be equal to integers, but the behavior of #= cannot be determined a priori. Since the behavior of #= does not imply a relationship of equivalence, the behavior of #hash is inconsequential.
Adding integers and floats to a set gets messed up. So we have two options... a) don't do it because it is intention obscuring... or b) make integers never be equal to floating point numbers.
Same deal with fractions and the like.
I wholeheartedly agree. We have used tolerance tests for floats since 1962 because equality tests are meaningless. = for floats should give error.
Cheers --Trygve
At 06:01 29.03.2006, Andres wrote:
Hello ncellier,
Tuesday, March 28, 2006, 7:25:46 AM, you wrote:
nic> [number hashing is broken] nic> What is your opinion ?
IMHO... sometimes it may be convenient to have 2 = 2.0, but since they represent different intentions, I would not expect that to happen. Why should a perfectly specified integer be equal to, possibly, the result of carrying calculations without infinite precision? What would it mean if theGreatDoublePrecisionResult = 2? Wouldn't it be more interesting (and precise) to ask laysWithin: epsilon from: 2?
In other words, comparing constants makes it look much simpler than what it really is. Reading something like anInteger = 2.0 would be, from my point of view, highly questionable because it is an assertion that an approximation has an *exact* value. Nonsense.
From a more pragmatic point of view, there is also the issue of 2 =
2.0, but things like
(1 bitShift: 1000) - 1
cannot be equal to any floating point number supported by common hardware. Thus, exploiting anInteger = aFloat is intention obscuring by definition since it may or may not work depending on the integer. Again, highly questionable.
In short: floating point numbers *may* be equal to integers, but the behavior of #= cannot be determined a priori. Since the behavior of #= does not imply a relationship of equivalence, the behavior of #hash is inconsequential.
Adding integers and floats to a set gets messed up. So we have two options... a) don't do it because it is intention obscuring... or b) make integers never be equal to floating point numbers.
Same deal with fractions and the like.
-- Best regards, Andres mailto:sqrmax@optonline.net
same here
I always use aFloat closeTo: bFloat
Stef
closeTo: is not bad, but 0.0001 accuracy is really something arbitrary. An optional accuracy argument with default value 0.0001 would be better, like Andres suggested:
laysWithin: epsilon from: 2
Back to Andres very clever mail :
In other words, comparing constants makes it look much simpler than what it really is. Reading something like anInteger = 2.0 would be, from my point of view, highly questionable because it is an assertion that an approximation has an *exact* value. Nonsense.
That is the very right answer to something i said but did not find satisfying, if not stupid : i said Fraction are more general than Float, so Float could eventually convert to Fraction for doing arithmetics... ksss
The other point of view is to say that Float are inexact arithmetic, Fraction and Integer are exact.
And in this case, adding something exact with something inexact gives me something inexact. So this is a good reason for converting to Float.
Other points were already discussed, and we now have all elements in mind and must make a decision between the three possibilities: a) let things as is with broken Set and hash (meaning go to hell with your mixed Set) b) change int=float to be never true (implementation very easy, possible nasty side effect in existing code) c) change int=float to be true only if exact representation are equal (implementation already on mantis, but great hash code slow down)
What is the right place for such a vote ? Squeak chat ? squeak-dev ? mantis ?
Nicolas
Andres Valloud wrote:
IMHO... sometimes it may be convenient to have 2 = 2.0, but since they represent different intentions, I would not expect that to happen.
What about magnitude comparisons (<, >, <=, >=)? For example when using (perfectly well-defined) binary search algorithms on mixed number representations it may be more than merely convenient to have 2 = 2.0.
Cheers, - Andreas
Why should a perfectly specified integer be equal to, possibly, the result of carrying calculations without infinite precision? What would it mean if theGreatDoublePrecisionResult = 2? Wouldn't it be more interesting (and precise) to ask laysWithin: epsilon from: 2?
In other words, comparing constants makes it look much simpler than what it really is. Reading something like anInteger = 2.0 would be, from my point of view, highly questionable because it is an assertion that an approximation has an *exact* value. Nonsense.
From a more pragmatic point of view, there is also the issue of 2 =
2.0, but things like
(1 bitShift: 1000) - 1
cannot be equal to any floating point number supported by common hardware. Thus, exploiting anInteger = aFloat is intention obscuring by definition since it may or may not work depending on the integer. Again, highly questionable.
In short: floating point numbers *may* be equal to integers, but the behavior of #= cannot be determined a priori. Since the behavior of #= does not imply a relationship of equivalence, the behavior of #hash is inconsequential.
Adding integers and floats to a set gets messed up. So we have two options... a) don't do it because it is intention obscuring... or b) make integers never be equal to floating point numbers.
Same deal with fractions and the like.
Hello Andreas,
Wednesday, March 29, 2006, 6:36:47 PM, you wrote:
AR> What about magnitude comparisons (<, >, <=, >=)? For example when AR> using (perfectly well-defined) binary search algorithms on mixed AR> number representations it may be more than merely convenient to AR> have 2 = 2.0.
Well... you can use aFloat > 2.0 as well, and in this case expressing intentions clearly would take just two extra characters. I find it hard to justify lack of proper expression because of unwillingness to type ".0". And besides it will be faster! :)
The Scheme community has a process called SRFIs which models on IETF RFCs to explore models and extensions to the standard language (I was one of the founding editors of SRFIs). Although Scheme is not (usually) OO, it is dynamically typed and has many of the same issues as Smalltalk.
In particular, they have recently been discussing many of these issues as part of SRFI-77, at: http://srfi.schemers.org/srfi-77/
In the Document, look at the Issues under the Design Rationalle section.
You may also find much of the archived discussion relevant.
../Dave
Thanks for this interesting link. I see we share same problems indeed, and maybe your reflexion is more advanced on the subject.
I do not think Smalltalkers are ready for duplicated arithmetic operators (float vs fix, exact vs inexact), they would rather prefer an explicit conversion. So maybe we won't share all our respective solutions, but we may at least for the generic arithmetic part.
As an interesting example, i never answered the question for complex with null imaginary part: should i keep the result complex or convert it to real ? Squeak keep it complex, visualworks convert it. The exact/inexact case you distinguish sounds a good proposition to me. But things are not so obvious... For example (* (1.0+0i) (1.0+0i) ) might well answer false to real? , unless (* 1.0 0) result in an exact zero (what it should do if you think of it).
Nicolas
Le Jeudi 30 Mars 2006 18:52, Dave Mason a écrit :
The Scheme community has a process called SRFIs which models on IETF RFCs to explore models and extensions to the standard language (I was one of the founding editors of SRFIs). Although Scheme is not (usually) OO, it is dynamically typed and has many of the same issues as Smalltalk.
In particular, they have recently been discussing many of these issues as part of SRFI-77, at: http://srfi.schemers.org/srfi-77/
In the Document, look at the Issues under the Design Rationalle section.
You may also find much of the archived discussion relevant.
../Dave
squeak-dev@lists.squeakfoundation.org