The Trunk: Kernel-nice.398.mcz

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

The Trunk: Kernel-nice.398.mcz

commits-2
Nicolas Cellier uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-nice.398.mcz

==================== Summary ====================

Name: Kernel-nice.398
Author: nice
Time: 13 February 2010, 4:31:00.389 am
UUID: f1525362-d01a-214c-94fe-2bb3a5fffa89
Ancestors: Kernel-nice.397

Fix
http://bugs.squeak.org/view.php?id=3360
http://bugs.squeak.org/view.php?id=3374
http://bugs.squeak.org/view.php?id=6601

hash and = are now reconciled for numbers.
= is now transitive for numbers.

WARNING: now, tests like (1/10 = 0.1) will answer false.
This is expected, and more than expected, this is wanted.
Float are inexact and testing for strict equality is not a clever thing to do.
All this has been longly debated at http://permalink.gmane.org/gmane.comp.lang.smalltalk.pharo.devel/10642
http://thread.gmane.org/gmane.comp.lang.smalltalk.sapphire.devel/10223/focus=10228

Please, read carefully this thread to make an opinion before raising your voice.
It can break code eventually,  so I'm all ears to real case, and willing to help fixing. But please, real examples, not theoretical (after 8 month change in Pharo, I'm not aware of further complaints).


=============== Diff against Kernel-nice.397 ===============

Item was changed:
  ----- Method: ScaledDecimal>><= (in category 'comparing') -----
  <= operand
  "Implementation of Number 'comparing' method."
  (operand isKindOf: ScaledDecimal) ifTrue: [^ fraction <= operand asFraction].
+ ^ operand adaptToScaledDecimal: self andCompare: #<=!
- ^ operand adaptToScaledDecimal: self andSend: #<=!

Item was added:
+ ----- Method: Float>>adaptToScaledDecimal:andCompare: (in category 'converting') -----
+ adaptToScaledDecimal: rcvr andCompare: selector
+ "If I am involved in comparison with a scaled Decimal, convert myself to a
+ Fraction. This way, no bit is lost and comparison is exact."
+
+ self isFinite
+ ifFalse: [
+ selector == #= ifTrue: [^false].
+ selector == #~= ifTrue: [^true].
+ self isNaN ifTrue: [^ false].
+ (selector = #< or: [selector = #'<='])
+ ifTrue: [^ self positive].
+ (selector = #> or: [selector = #'>='])
+ ifTrue: [^ self positive not].
+ ^self error: 'unknow comparison selector'].
+
+ ^ rcvr perform: selector with: self asTrueFraction!

Item was changed:
  ----- Method: Integer>>>= (in category 'comparing') -----
  >= aNumber
  aNumber isInteger ifTrue:
  [self negative == aNumber negative
  ifTrue: [self negative
  ifTrue: [^(self digitCompare: aNumber) <= 0]
  ifFalse: [^(self digitCompare: aNumber) >= 0]]
  ifFalse: [^ aNumber negative]].
+ ^ aNumber adaptToInteger: self andCompare: #>=!
- ^ aNumber adaptToInteger: self andSend: #>=!

Item was changed:
  ----- Method: Float>>>= (in category 'comparing') -----
  >= aNumber
  "Primitive. Compare the receiver with the argument and return true
  if the receiver is greater than or equal to the argument. Otherwise return
  false. Fail if the argument is not a Float. Optional. See Object documentation
  whatIsAPrimitive. "
 
  <primitive: 46>
+ ^ aNumber adaptToFloat: self andCompare: #>=!
- ^ aNumber adaptToFloat: self andSend: #>=!

Item was changed:
  ----- Method: ScaledDecimal>>< (in category 'comparing') -----
  < operand
  "Implementation of Number 'comparing' method."
  (operand isKindOf: ScaledDecimal) ifTrue: [^ fraction < operand asFraction].
+ ^ operand adaptToScaledDecimal: self andCompare: #<!
- ^ operand adaptToScaledDecimal: self andSend: #<!

Item was changed:
  ----- Method: Fraction>>>= (in category 'comparing') -----
  >= aNumber
  aNumber isFraction ifTrue:
  [^ numerator * aNumber denominator >= (aNumber numerator * denominator)].
+ ^ aNumber adaptToFraction: self andCompare: #>=!
- ^ aNumber adaptToFraction: self andSend: #>=!

Item was changed:
  ----- Method: ScaledDecimal>>= (in category 'comparing') -----
  = comparand
  "Implementation of Number 'comparing' method."
  comparand isNumber ifFalse: [^ false].
  (comparand isKindOf: ScaledDecimal) ifTrue: [^ fraction = comparand asFraction].
+ ^ comparand adaptToScaledDecimal: self andCompare: #=!
- ^ comparand adaptToScaledDecimal: self andSend: #=!

Item was changed:
  ----- Method: ScaledDecimal>>> (in category 'comparing') -----
  > operand
  "Implementation of Number 'comparing' method."
  (operand isKindOf: ScaledDecimal) ifTrue: [^ fraction > operand asFraction].
+ ^ operand adaptToScaledDecimal: self andCompare: #>!
- ^ operand adaptToScaledDecimal: self andSend: #>!

Item was added:
+ ----- Method: Float>>adaptToInteger:andCompare: (in category 'converting') -----
+ adaptToInteger: rcvr andCompare: selector
+ "If I am involved in comparison with an Integer, convert myself to a
+ Fraction. This way, no bit is lost and comparison is exact."
+
+ self isFinite
+ ifFalse: [
+ selector == #= ifTrue: [^false].
+ selector == #~= ifTrue: [^true].
+ self isNaN ifTrue: [^ false].
+ (selector = #< or: [selector = #'<='])
+ ifTrue: [^ self positive].
+ (selector = #> or: [selector = #'>='])
+ ifTrue: [^ self positive not].
+ ^self error: 'unknow comparison selector'].
+
+ "Try to avoid asTrueFraction because it can cost"
+ selector == #= ifTrue: [
+ self fractionPart = 0.0 ifFalse: [^false]].
+ selector == #~= ifTrue: [
+ self fractionPart = 0.0 ifFalse: [^true]].
+
+ ^ rcvr perform: selector with: self asTrueFraction!

Item was added:
+ ----- Method: Object>>adaptToFraction:andCompare: (in category 'converting') -----
+ adaptToFraction: rcvr andCompare: selector
+ "If I am involved in comparison with a Fraction.
+ Default behaviour is to process comparison as any other selectors."
+ ^ self adaptToFraction: rcvr andSend: selector!

Item was added:
+ ----- Method: Number>>adaptToFloat:andCompare: (in category 'converting') -----
+ adaptToFloat: rcvr andCompare: selector
+ "If I am involved in comparison with a Float, convert rcvr to a
+ Fraction. This way, no bit is lost and comparison is exact."
+
+ rcvr isFinite
+ ifFalse: [
+ selector == #= ifTrue: [^false].
+ selector == #~= ifTrue: [^true].
+ rcvr isNaN ifTrue: [^ false].
+ (selector = #< or: [selector = #'<='])
+ ifTrue: [^ rcvr positive not].
+ (selector = #> or: [selector = #'>='])
+ ifTrue: [^ rcvr positive].
+ ^self error: 'unknow comparison selector'].
+
+ ^ rcvr asTrueFraction perform: selector with: self!

Item was changed:
  ----- Method: Float>>hash (in category 'comparing') -----
  hash
+ "Hash is reimplemented because = is implemented. Both words of the float are used. (The bitShift:'s ensure that the intermediate results do not become a large integer.) Care is taken to answer same hash as an equal Integer."
- "Hash is reimplemented because = is implemented. Both words of the float are used; 8 bits are removed from each end to clear most of the exponent regardless of the byte ordering. (The bitAnd:'s ensure that the intermediate results do not become a large integer.) Slower than the original version in the ratios 12:5 to 2:1 depending on values. (DNS, 11 May, 1997)"
 
+ (self isFinite and: [self fractionPart = 0.0]) ifTrue: [^self truncated hash].
+ ^ ((self basicAt: 1) bitShift: -4) +
+   ((self basicAt: 2) bitShift: -4)
- ^ (((self basicAt: 1) bitAnd: 16r00FFFF00) +
-   ((self basicAt: 2) bitAnd: 16r00FFFF00)) bitShift: -8
  !

Item was added:
+ ----- Method: Object>>adaptToFloat:andCompare: (in category 'converting') -----
+ adaptToFloat: rcvr andCompare: selector
+ "If I am involved in comparison with a Float.
+ Default behaviour is to process comparison as any other selectors."
+ ^ self adaptToFloat: rcvr andSend: selector!

Item was changed:
  ----- Method: Fraction>>hash (in category 'comparing') -----
  hash
+ "Hash is reimplemented because = is implemented.
+ Care is taken that a Fraction equal to a Float also have an equal hash"
- "Hash is reimplemented because = is implemented."
 
+ | tmp |
+ denominator isPowerOfTwo ifTrue: [
+ "If denominator is a power of two, I can be exactly equal to a Float
+ Assume the fraction is already reduced"
+ tmp := self asFloat.
+ tmp isFinite ifTrue: [^tmp hash]].
  ^numerator hash bitXor: denominator hash!

Item was changed:
  ----- Method: Integer>><= (in category 'comparing') -----
  <= aNumber
  aNumber isInteger ifTrue:
  [self negative == aNumber negative
  ifTrue: [self negative
  ifTrue: [^ (self digitCompare: aNumber) >= 0]
  ifFalse: [^ (self digitCompare: aNumber) <= 0]]
  ifFalse: [^ self negative]].
+ ^ aNumber adaptToInteger: self andCompare: #<=!
- ^ aNumber adaptToInteger: self andSend: #<=!

Item was added:
+ ----- Method: Object>>adaptToScaledDecimal:andCompare: (in category 'converting') -----
+ adaptToScaledDecimal: rcvr andCompare: selector
+ "If I am involved in comparison with a ScaledDecimal.
+ Default behaviour is to process comparison as any other selectors."
+ ^ self adaptToScaledDecimal: rcvr andSend: selector!

Item was changed:
  ----- Method: Float>><= (in category 'comparing') -----
  <= aNumber
  "Primitive. Compare the receiver with the argument and return true
  if the receiver is less than or equal to the argument. Otherwise return
  false. Fail if the argument is not a Float. Optional. See Object
  documentation whatIsAPrimitive."
 
  <primitive: 45>
+ ^ aNumber adaptToFloat: self andCompare: #<=!
- ^ aNumber adaptToFloat: self andSend: #<=!

Item was changed:
  ----- Method: Fraction>><= (in category 'comparing') -----
  <= aNumber
  aNumber isFraction ifTrue:
  [^ numerator * aNumber denominator <= (aNumber numerator * denominator)].
+ ^ aNumber adaptToFraction: self andCompare: #<=!
- ^ aNumber adaptToFraction: self andSend: #<=!

Item was changed:
  ----- Method: Integer>>< (in category 'comparing') -----
  < aNumber
  aNumber isInteger ifTrue:
  [self negative == aNumber negative
  ifTrue: [self negative
  ifTrue: [^ (self digitCompare: aNumber) > 0]
  ifFalse: [^ (self digitCompare: aNumber) < 0]]
  ifFalse: [^ self negative]].
+ ^ aNumber adaptToInteger: self andCompare: #<!
- ^ aNumber adaptToInteger: self andSend: #<!

Item was changed:
  ----- Method: ScaledDecimal>>>= (in category 'comparing') -----
  >= operand
  "Implementation of Number 'comparing' method."
  (operand isKindOf: ScaledDecimal) ifTrue: [^ fraction >= operand asFraction].
+ ^ operand adaptToScaledDecimal: self andCompare: #>=!
- ^ operand adaptToScaledDecimal: self andSend: #>=!

Item was changed:
  ----- Method: Integer>>= (in category 'comparing') -----
  = aNumber
  aNumber isNumber ifFalse: [^ false].
  aNumber isInteger ifTrue:
  [aNumber negative == self negative
  ifTrue: [^ (self digitCompare: aNumber) = 0]
  ifFalse: [^ false]].
+ ^ aNumber adaptToInteger: self andCompare: #=!
- ^ aNumber adaptToInteger: self andSend: #=!

Item was changed:
  ----- Method: Integer>>> (in category 'comparing') -----
  > aNumber
  aNumber isInteger ifTrue:
  [self negative == aNumber negative
  ifTrue: [self negative
  ifTrue: [^(self digitCompare: aNumber) < 0]
  ifFalse: [^(self digitCompare: aNumber) > 0]]
  ifFalse: [^ aNumber negative]].
+ ^ aNumber adaptToInteger: self andCompare: #>!
- ^ aNumber adaptToInteger: self andSend: #>!

Item was changed:
  ----- Method: Float>>< (in category 'comparing') -----
  < aNumber
  "Primitive. Compare the receiver with the argument and return true
  if the receiver is less than the argument. Otherwise return false.
  Fail if the argument is not a Float. Essential. See Object documentation
  whatIsAPrimitive."
 
  <primitive: 43>
+ ^ aNumber adaptToFloat: self andCompare: #<!
- ^ aNumber adaptToFloat: self andSend: #<!

Item was changed:
  ----- Method: Fraction>>< (in category 'comparing') -----
  < aNumber
  aNumber isFraction ifTrue:
  [^ numerator * aNumber denominator < (aNumber numerator * denominator)].
+ ^ aNumber adaptToFraction: self andCompare: #<!
- ^ aNumber adaptToFraction: self andSend: #<!

Item was changed:
  ----- Method: Float>>= (in category 'comparing') -----
  = aNumber
  "Primitive. Compare the receiver with the argument and return true
  if the receiver is equal to the argument. Otherwise return false.
  Fail if the argument is not a Float. Essential. See Object documentation
  whatIsAPrimitive."
 
  <primitive: 47>
  aNumber isNumber ifFalse: [^ false].
+ ^ aNumber adaptToFloat: self andCompare: #=!
- ^ aNumber adaptToFloat: self andSend: #=!

Item was changed:
  ----- Method: Fraction>>= (in category 'comparing') -----
  = aNumber
  aNumber isNumber ifFalse: [^ false].
  aNumber isFraction
  ifTrue: [numerator = 0 ifTrue: [^ aNumber numerator = 0].
  ^ (numerator * aNumber denominator) =
  (aNumber numerator * denominator)
  "Note: used to just compare num and denom,
  but this fails for improper fractions"].
+ ^ aNumber adaptToFraction: self andCompare: #=!
- ^ aNumber adaptToFraction: self andSend: #=!

Item was added:
+ ----- Method: Object>>adaptToInteger:andCompare: (in category 'converting') -----
+ adaptToInteger: rcvr andCompare: selector
+ "If I am involved in comparison with an Integer.
+ Default behaviour is to process comparison as any other selectors."
+ ^ self adaptToInteger: rcvr andSend: selector!

Item was added:
+ ----- Method: Float>>adaptToFraction:andCompare: (in category 'converting') -----
+ adaptToFraction: rcvr andCompare: selector
+ "If I am involved in comparison with a Fraction, convert myself to a
+ Fraction. This way, no bit is lost and comparison is exact."
+
+ self isFinite
+ ifFalse: [
+ selector == #= ifTrue: [^false].
+ selector == #~= ifTrue: [^true].
+ self isNaN ifTrue: [^ false].
+ (selector = #< or: [selector = #'<='])
+ ifTrue: [^ self positive].
+ (selector = #> or: [selector = #'>='])
+ ifTrue: [^ self positive not].
+ ^self error: 'unknow comparison selector'].
+
+ "Try to avoid asTrueFraction because it can cost"
+ selector == #= ifTrue: [
+ rcvr denominator isPowerOfTwo ifFalse: [^false]].
+ selector == #~= ifTrue: [
+ rcvr denominator isPowerOfTwo ifFalse: [^true]].
+
+ ^ rcvr perform: selector with: self asTrueFraction!

Item was changed:
  ----- Method: Float>>> (in category 'comparing') -----
  > aNumber
  "Primitive. Compare the receiver with the argument and return true
  if the receiver is greater than the argument. Otherwise return false.
  Fail if the argument is not a Float. Essential. See Object documentation
  whatIsAPrimitive."
 
  <primitive: 44>
+ ^ aNumber adaptToFloat: self andCompare: #>!
- ^ aNumber adaptToFloat: self andSend: #>!

Item was changed:
  ----- Method: Fraction>>> (in category 'comparing') -----
  > aNumber
  aNumber isFraction ifTrue:
  [^ numerator * aNumber denominator > (aNumber numerator * denominator)].
+ ^ aNumber adaptToFraction: self andCompare: #>!
- ^ aNumber adaptToFraction: self andSend: #>!