Float/Fraction Equality problem : should be transitive

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Float/Fraction Equality problem : should be transitive

Nicolas Cellier-3
"Try this: you get size of 2"

| q qf q2 |
q := 12346548789456/346874324768747.
qf := q asFloat.
q2 := qf asTrueFraction.
(Set with: q with: qf with: q2) size.

"Then try this, you get size of 1"

| q qf q2 |
q := 12346548789456/346874324768747.
qf := q asFloat.
q2 := qf asTrueFraction.
(Set with: qf with: q with: q2) size

"What's wrong ?"

I already discussed this on squeak-dev, the problem is that Float
should not equal a Fraction, except self asTrueFraction, otherwise
equality is no more transitive.

Same can be illustrated with LargeInteger...

See http://bugs.impara.de/view.php?id=3374, where i propose converting
Float asTrueFraction in order to compare Float to Integer and Fraction,
instead of converting Integer and Fraction asFloat...

Only comparison operators (= ~= < <= > >=) should use asTrueFraction
conversion.
Arithmetic + * - / should still convert asFloat.
Rationale: mixing exact (Integer or Fraction) arithmetic with inexact
(Float) arithmetic give an inexact result (Float).

Nicolas


Reply | Threaded
Open this post in threaded view
|

Re: Float/Fraction Equality problem : should be transitive

Nicolas Cellier-3
I attach a proposition to handle Float/Fraction/Integer equality in a
way preserving transitivity  property.

Now we can have two numbers such that (a-b) isZero is true - due to
inexact arithmetic, but (a = b) is false.

Nicolas



| package |
package := Package name: 'FloatComparePatch'.
package paxVersion: 1;
        basicComment: ''.


package methodNames
        add: #ArithmeticValue -> #compareWithFloat:;
        add: #ArithmeticValue -> #compareWithFraction:;
        add: #ArithmeticValue -> #compareWithInteger:;
        add: #Float -> #=;
        add: #Float -> #compareWithFraction:;
        add: #Float -> #compareWithInteger:;
        add: #Float -> #greaterThanFraction:;
        add: #Float -> #greaterThanInteger:;
        add: #Fraction -> #=;
        add: #Fraction -> #compareWithFloat:;
        add: #Fraction -> #greaterThanFloat:;
        add: #Integer -> #=;
        add: #Integer -> #compareWithFloat:;
        add: #Integer -> #greaterThanFloat:;
        yourself.

package binaryGlobalNames: (Set new
        yourself).

package globalAliases: (Set new
        yourself).

package setPrerequisites: (IdentitySet new
        add: 'Object Arts\Dolphin\Base\Dolphin';
        yourself).

package!

"Class Definitions"!


"Global Aliases"!


"Loose Methods"!

!ArithmeticValue methodsFor!

compareWithFloat: aFloat
        ^(self - aFloat) isZero!

compareWithFraction: aFraction
        ^(self - aFraction) isZero!

compareWithInteger: anInteger
        ^(self - anInteger) isZero! !
!ArithmeticValue categoriesFor: #compareWithFloat:!double
dispatch!public! !
!ArithmeticValue categoriesFor: #compareWithFraction:!double
dispatch!public! !
!ArithmeticValue categoriesFor: #compareWithInteger:!double
dispatch!public! !

!Float methodsFor!

= comperand
        "Answer whether the receiver is numerically equivalent to the
argument,
        comperand (e.g. 1 = 1.0 is true).

        Primitive failure reasons:
                0 - aNumber is not a SmallInteger or a Float.

        May also raise a floating point exception."

        <primitive: 163>
        ^comperand understandsArithmetic and: [comperand compareWithFloat:
self]
        "^super = comperand"!

compareWithFraction: aFraction
        ^self asTrueFraction = aFraction!

compareWithInteger: anInteger
        ^self asTrueFraction = anInteger!

greaterThanFraction: aFraction
        "Private - Answer whether the receiver is greater than the known
Fraction, aFraction."

        ^aFraction < self asTrueFraction!

greaterThanInteger: anInteger
        "Private - Answer whether the receiver is greater than the known
Integer, anInteger."

        ^anInteger < self asTrueFraction! !
!Float categoriesFor: #=!comparing!public! !
!Float categoriesFor: #compareWithFraction:!double dispatch!public! !
!Float categoriesFor: #compareWithInteger:!double dispatch!public! !
!Float categoriesFor: #greaterThanFraction:!double dispatch!private! !
!Float categoriesFor: #greaterThanInteger:!double dispatch!private! !

!Fraction methodsFor!

= comparand
        ^comparand understandsArithmetic
                and: [comparand compareWithFraction: self]!

compareWithFloat: aFloat
        ^aFloat asTrueFraction = self!

greaterThanFloat: aFloat
        "Private - Answer whether the receiver is greater than the known
Float, aFloat"

        ^aFloat asTrueFraction < self! !
!Fraction categoriesFor: #=!comparing!public! !
!Fraction categoriesFor: #compareWithFloat:!double dispatch!public! !
!Fraction categoriesFor: #greaterThanFloat:!double dispatch!private! !

!Integer methodsFor!

= comparand
        ^comparand understandsArithmetic
                and: [comparand compareWithInteger: self]!

compareWithFloat: aFloat
        ^aFloat asTrueFraction = self!

greaterThanFloat: aFloat
        "Private - Answer whether the receiver is greater than the known
Float, aFloat."

        ^aFloat asTrueFraction < self! !
!Integer categoriesFor: #=!comparing!public! !
!Integer categoriesFor: #compareWithFloat:!double dispatch!public! !
!Integer categoriesFor: #greaterThanFloat:!double dispatch!private! !

"End of package definition"!

"Source Globals"!

"Classes"!

"Binary Globals"!