> From: Mathieu Bouchard [mailto:matju / sympatico.ca] [..] > I'm not proposing that +Infinity should be indistinguishable from > -Infinity. I'm asking why is +Infinity == +Infinity and -Infinity == > -Infinity. Well, basically the Numeric class hierarchy puts a nice OO-rapper around the native C types integer and double and adds Bignum to it and for the most part this was very well done - the identity methods seem the follow pattern * x == y - if x,y are C-equal * x.eql? y - if x,y are C-equal and no type conversion is necessary * x.equal? y - if x,y are id-equal (this effects Float and Bignum) Since C's double has the unique constants +/- Infinity and NaN you need to guarantee that 1/0.0 == 1/0.0, 1/-0.0 == 1/-0.0,( 1/0.0) * 0 == (1/0.0) * 0 (== NaN) otherwise you would break this pattern. Anthing else would make the special value logic essentially unusable IMO. This is not really relevant here but I sort believe that at least one of Ruby's equal methods should have been a class method - something like Float.identical?(1, 1.0) # => true This would have eliminated the problematic asymmetry of methods receiver versus method argument and it makes generally more sense anyway - the only reason against this are the extra key strokes. [...] > > The exception raising behavior of zero-division and comparison > > is type specific on the method receiver end > > 2**29 <=> 1/0.0 # => -1 since 2**29 is a Fixnum > > 2**33 <=> 1/0.0 # FloatDomainError: Infinity since 2**33 is Bignum > > this should return -1 since although +Infinity is not a precise number, it > is greater than all other finite values and -Infinity. (it is however > noncomparable to itself and to NaN) I don't believe that this is such a good idea - you would get 10**400 > 1.0 # => true 10**400 + 10**400 > 1.0 + 10**400 # => false since 1.0 + 10**400 is equal to Floats Infinity but the left hand side is still an ordinary Bignum which amounts to the mathematical equivalent of a containment breach during a nuclear reactor melt-down. > > 1/ 0 # a ZeroDivisionError is raised for any Integer type > > This is ok, because the Integer domain has no equivalent of Infinities and > NaNs. Yes .. > > I actually have a Ruby hack of logical +/- Infinity's constants. > > The semantics is closer to +/- nil and but you can perform limited > > Arithmetic on them - I made them instances of +/- Infinity Classes > > sub-typed from Numeric but this is not really necessary. (the are > > necessarily smaller/bigger then floats infinities). > > May I see that? Btw, what are +nil and -nil ??? I attached something below - the reason for calling them +/- nil is that they could be the return values of [].min, [].man etc. which currently return nil and if +/- Infinity would act as logical false values you probably would not break any existing script. > > methods input of any numeric object (except for Rational, which > > is an eager beaver and tries to process input it does not > > understand instead of calling coerce). > > Do you think Rational should be fixed to act, ehm, more rationally? Don't know - the current behavior also breaks theoretical stuff like integral or field extension of the Integer Class by ``adding sqrt(2)'' etc. but this is not that problematic IMO. To beat a dead horse of mine what Ruby is really missing is support for Modules (and consequently Classes) parameterized by Module constant with a instantiation logic. [...] Christoph --------------- Sorry this is quite longish - basically it was an exploration of the Singleton class construction and I wanted to see on how far the poor men's version of multi-dispatch (a.k.a. coerce) gets you - running examples with +/-infty as input is quite usefully for debugging and it is a better (that means fewer if-then-else constructs in your code) return value for certain method then nil, -1 etc. ------------ require 'infinity' module Enumerable def max; inject(Infinity.minus) {|a,b| (a > b ) ? a : b } end end p [].max # => -infty p [3,5,-9].max # => 5 class Someclass Minimalvalue= Infinity.minus # ..... def size if empty? Minimalvalue else something_else end end end ------------ ###### infinity.rb ############################ # Copyright (c) 2001 by Christoph Rippel # Licensed under the same license as Ruby ############################################### class Infinity < Numeric; end ###### Thread safety ######## unless Infinity.instance_eval { @__initialized__ } Thread.critical = true begin class Numeric # The finite?, infinite? methods and the Zero, One constants # are nice but not necessary. Note that the Fixnums Zero, One # constants are the Zero, One constants of the whole Numeric # hierarchy - the OO-manifestation that a (commutative) ring # is the same thing as a (commutative) algebra. def finite?; true end def infinite?; false end Zero = 0 One = 1 end class Float Zero = 0.0 One = 1.0 end module InfinityError # The Error handling regime is probably brain dead .. class AdditionError < TypeError; end class SubtractionError < TypeError; end class InfinityDivisionError < TypeError; end class ZeroMultiplicationError < TypeError; end class ZeroDivisionError < ZeroDivisionError; end def addition; raise AdditionError end def subtraction; raise SubtractionError end def infinityDiv(other); raise InfinityDivisionError end def zeroMul(other); raise ZeroMultiplicationError end def zeroDiv(other); raise ZeroDivisionError end def inspect; 'InfinityError handler' end def to_s; 'InfinityError handler' end end class Infinity # Common instances methods def finite?; false end def infinite?; true end def integer?; false end def succ; self end def next; self end def zero?; false end # Utility methods to access the Instance and # Error handling object - rarely used def error_handler; type::Ehandler end def instance; type::Instance end # Thread safety @__initialized__ = true class << self def minus; InfinityMinus::Instance end def plus; InfinityPlus::Instance end end end class InfinityMinus < Infinity # It is faster to define the less commonly methods as # instances methods - incidentally they are mostly # shared between the Instance and its mirror. Instance = self.new Ehandler = Object.new.extend InfinityError def inspect; ' -infty' end def to_s; ' -infty' end def sgn; -1 end def times(&prog); Instance end end class InfinityPlus < Infinity Instance = self.new Ehandler = Object.new.extend InfinityError def inspect; ' infty' end def to_s; ' infty' end def sgn; 1 end def times(&prog) prog.call(self) while true Instance end end class << Infinity.minus # Defining the Mirror as a singleton constant is notably # faster - the luxury of supporting infinite multiplication # and division is expensive because we need to create a # new coercion Array every time coerce is called. Minus = Infinity.minus Plus = Infinity.plus Mirror = InfinityMinus.new def coerce(other); [Mirror, other] end def finite?; false end def <(other); !(equal? other) end def >(other); false end def <=>(other); (equal? other) ? 0 : -1 end # defining -@ is not necessary but more efficient def -@; Plus end def +(other); (other.equal? Plus) ? Ehandler.addition : Minus end def -(other); (other.equal? Minus) ? Ehandler.subtraction : Minus end def *(other); return Plus if other < 0 return Minus if other > 0 return Ehandler.zeroMul(other) end def /(other) return Ehandler.infinityDiv(other) unless other.finite? return Plus if other < 0 return Minus if other > 0 return Ehandler.zeroDiv(other) end class << Mirror Minus = Infinity.minus Plus = Infinity.plus def <(other); false end def >(other); true end def <=>(other); 1 end def +(other); Minus end def -(other); Plus end def *(other) return Plus if other > 0 return Minus if other < 0 return Ehandler.zeroMul(other) end def /(other) ; other.type::Zero end def %(other) ; (other > 0 ) ? Minus : other end # def remainder(other); other end # Ruby ignores the previous line end end class << Infinity.plus Plus = Infinity.plus Mirror = InfinityPlus.new Minus = Infinity.minus def coerce(other); [Mirror, other] end def finite?; false end def <(other); false end def >(other); !(equal? other) end def <=>(other); (equal? other) ? 0 : 1 end def -@; Minus end def +(other); (other.equal? Minus) ? Ehandler.addition : Plus end def -(other); (other.equal? Plus) ? Ehandler.subtraction : Plus end def *(other); return Plus if other > 0 return Minus if other < 0 return Ehandler.zeroMul(other) end def /(other) return Ehandler.infinityDiv(other) unless other.finite? return Plus if other > 0 return Minus if other < 0 return Ehandler.zeroDiv(other) end class << Mirror Plus = Infinity.plus Minus = Infinity.minus def <(other); true end def >(other); false end def <=>(other); -1 end def +(other); Plus end def -(other); Plus end def *(other); return Plus if other > 0 return Minus if other < 0 return Ehandler.zeroMul(other) end def /(other); (other.type)::Zero end def %(other) ; (other < 0 ) ? Plus : other end end end class << Infinity undef_method :new end ensure Thread.critical = false end end def Infinity.leftlogic_initialize Thread.critical = true begin ::Infinity.class_eval (%{ def to_truth; false end def &(other); ::NIL & other end def ^ (other); ::NIL ^ other end def | (other); ::NIL | other end }) ensure ::Infinity.instance_eval (%{ def leftlogic_initialize; true end }) Thread.critical = false end true end ------------ ###### test.rb ################################ require 'infinity' mi = Infinity.minus pi = Infinity.plus mi = Infinity.minus pi = Infinity.plus def p_ary (*ary); puts ary.join ", " end x = mi x -= 25 x *= -13.0 y = 344.9999 y /= pi p_ary "simple test", x, y, 34**34 / mi p_ary "x+ -infty", mi + mi, mi - pi, mi + 1, 1.0+ mi p_ary "x+ infty", pi + pi, pi - mi, pi + 2.0, -3**24 + pi p_ary "-infty <=> x", mi <=> mi, mi <=> 0, mi <=> pi p_ary " x <=> - infty", 0 <=> mi, pi <=> mi p_ary "infty <=> x", pi <=> mi, pi <=> 0, pi <=> pi p_ary " x <=> infty", mi <=> pi, 31.3 <=> pi p_ary "-infty < x", mi < mi, mi < 0, mi < pi p_ary " x < - infty", 0 < mi, pi < mi p_ary "infty < x", pi < mi, pi < 0, pi < pi p_ary " x < infty", mi < pi, 31.3 < pi p_ary "-infty > x", mi > mi, mi > 0, mi > pi p_ary " x > - infty", 0 > mi, pi > mi p_ary "infty > x", pi > mi, pi > 0, pi > pi p_ary " x > infty", mi > pi, 31.3 > pi p_ary "-infty == x", mi == mi, mi == 0, mi == pi p_ary " x == - infty", 0 == mi, pi == mi p_ary "infty == x", pi == mi, pi == 0, pi == pi p_ary " x == infty", mi == pi, 31.3 == pi p "test of Ehandlers and *,/ etc. " p " An alternative for the normal error-raising behavior" class << mi.error_handler def zeroMul(other); -other.type::One end def infinityDiv(other); -other.sgn end def zeroDiv(other); Infinity.minus end end class << pi.error_handler def zeroMul(other); other.type::One end def infinityDiv(other); other.sgn end def zeroDiv(other); Infinity.plus end end module InfinityError def addition; 0 end def subtraction; 0 end end p_ary "-infty*x", mi*mi, mi* (-1.0), mi* 0.0, mi * 23, mi*pi p_ary "-infty/x", mi/mi, mi/ (-1.0), mi/ 0.0, mi/ 1, mi/ pi p_ary "infty*x", pi*mi, pi* (-1.0), pi* 0.0, pi * 23, pi*pi p_ary "infty/x", pi/mi, pi/ (-1.0), pi/ 0.0, pi/ 1, pi/ pi p_ary "x*-infty*x", -2.3* mi,0.0 * mi, 23* mi p_ary "x/ -infty", -3.5/ mi, 0.0 /mi, 1/ mi, mi/ 1, pi/ mi p_ary "x*infty", -2.3* pi,0.0 * mi, 23* mi p_ary "x/ infty", mi/ - pi, 0.0 /pi, 1/ pi, pi/ pi p_ary "add", mi - mi, pi - pi, mi + pi, pi + mi Infinity.leftlogic_initialize p "left-logic test" p_ary pi ^ mi, pi & 64, mi | false