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