Hi,

In message "[ruby-talk:03202] Nan and Infinity"
    on 00/06/08, Dave Thomas <Dave / thomases.com> writes:
>If the result of a floating point operation is Nan or one of the
>Infinities, is there any way to tell apart from converting the number
>to a string.
>
>Would Float#isNan? be useful?

I had written floatinternal.rb to hack floating numbers. 
It includes Float#nan? and Float#inf?. 

Hope this helps,

-- gotoken

# Apologies for absence of documnetation. 

# floatinternal.rb # Author: gotoken require "rational" class FloatFormatError < StandardError def message "invalid floating point number internal format" end end class Integer def bin(fmt = "", width = 0, sep = ?_) represent(:bin, fmt, sep, width) end def oct(fmt = "", width = 0, sep = ?_) represent(:oct, fmt, sep, width) end def dec(fmt = "", width = 0, sep = ?_) represent(:dec, fmt, sep, width) end def hex(fmt = "", width = 0, sep = ?_) represent(:hex, fmt, sep, width) end private def represent(base, fmt, sep, width) case base when :bin, :oct, :dec fmt =~ /\A\+?(?:[0]?[1-9][0-9]*)?\Z/ or raise ArgumentError, "invalid format `#{fmt}'" when :hex fmt =~ /\A\+?(?:[0]?[1-9][0-9]*)?[xX]?\Z/ or raise ArgumentError, "invalid format `#{fmt}'" end case base when :bin buf = "%#{fmt}b" % self when :oct buf = "%#{fmt}o" % self when :dec buf = "%#{fmt}d" % self when :hex fmt += "x" unless /[xX]\Z/ =~ fmt buf = "%#{fmt}" % self end if width > 0 buf[0].chr + buf[1..-1].reverse!.gsub(/#{"."*width}/,"\\&#{sep.chr}").reverse! else buf end end end class String def bin eval("0b" + (scan(/\A[01][01_]*/)[0])) end end class String def intern_double(base = 2) tmp = delete('_') res = catch(:error) do case base when 2 throw(:error) unless tmp =~ /\A[01]{64,64}\Z/ [tmp].pack("B*").unpack("G")[0] when 8 throw(:error) unless tmp =~ /\A[0-7]{32,32}\Z/ ["%016x" % tmp.oct].pack("h16").unpack("G")[0] when 16 throw(:error) unless tmp =~ /\A[0-9a-fA-F]{16,16}\Z/ [tmp].pack("h16").unpack("G")[0] else throw(:error) end end unless res raise FloatFormatError, "invalid floating point number internal format" end res end alias network_byte_order_float_format intern_double end class Array def intern_double(base = 2) if size != 3 || grep(String).size != 3 raise FloatFormatError, "invalid floating point number internal format" end s, e, m = self case base when 2 unless s =~ /\A[01]\Z/ && e =~ /\A[01]{11,11}\Z/ && m =~ /\A[01]{52,52}\Z/ raise FloatFormatError, "invalid floating point number internal format" end (s + e + m).intern_double when 8 unless s =~ /\A[01]\Z/ && e =~ /\A[0-7]{4,4}\Z/ && m =~ /\A[0-7]{18,18}\Z/ raise FloatFormatError, "invalid floating point number internal format" end (s.oct.bin + e.oct.bin("011") + e.oct.bin("052")).intern_double(2) when 16 unless s =~ /\A[01]\Z/ && e =~ /\A[0-9a-fA-F]{3,3}\Z/ && m =~ /\A[0-9a-fA-F]{13,13}\Z/ raise FloatFormatError, "invalid floating point number internal format" end (s.hex.bin + e.hex.bin("011") + e.hex.bin("052")).intern_double(2) end end end class Float T_ZERO = "ZERO".freeze T_NORMAL = "NORMAL".freeze T_DENORMAL = "DENORMAL".freeze T_INF = "INF".freeze T_NaN = "NaN".freeze DoubleInternal = Struct.new("DoubleInternal", "t", "s", "e", "m") def intern_str(base = 2) bstr = [self].pack("G") case base when 2 bstr.unpack("B64")[0] when 8 bstr.unpack("h16")[0].hex.oct("032") when 16 bstr.unpack("h16")[0] else raise ArgumentError, "illegal base `#{base}' (!= 2,8,16)" end end def bitstring(base = 2) bstr = [self].pack("G").unpack("B*")[0] case base when 2 [bstr[0].chr, bstr[1,11], bstr[12,52]] when 8 [bstr[0].chr, "%04o" % bstr[1,11].bin, "%018o" % bstr[12,52].bin] when 16 [bstr[0].chr, "%03x" % bstr[1,11].bin, "%013x" % bstr[12,52].bin] else raise ArgumentError, "illegal base `#{base}' (!= 2,8,16)" end end def decomp bstr = bitstring estr, mstr = bstr[1], bstr[2] s, e0, m0 = bstr[0][0]-?0, estr.bin, mstr.bin case e0 when 1..2046 # normal t = T_NORMAL when 0 # denormal t = m0 == 0 ? T_ZERO : T_DENORMAL when 2047 if m0 == 0 t = T_INF else t = T_NaN end end [t, s, e0, m0] end alias decompose decomp def mne t, s, e0, m0 = decomp case t when T_NORMAL e = e0 - 1023 m = Rational(m0 + 2**52, 2**52) when T_ZERO e = 0 m = Rational(0,1) when T_DENORMAL e = -1022 m = Rational(m0, 2**52) when T_NaN return DoubleInternal.new(t, nil, e, m) end DoubleInternal.new(t, -2*s+1, e, m) end alias mantissa_and_exponent mne def nan? decomp[0] == T_NaN end def inf? decomp[0] == T_INF end end if __FILE__ == $0 require "xmp" xmp <<-EOS 0.1.mne 0.1.bitstring 0.1.bitstring(8).collect{|i| i.hex.bin} 0.1.bitstring(8) (0.0/0.0).nan? (0.0/1.0).nan? (0.0/0.0).inf? (0.0/1.0).inf? EOS end