class Float < Numeric def **(other) Rubinius.primitive :float_pow if other.is_a?(Float) && self < 0 && other != other.round return Complex.new(self, 0) ** other end b, a = math_coerce other a ** b end def imaginary 0 end def numerator if nan? NAN elsif infinite? == 1 INFINITY elsif infinite? == -1 -INFINITY else super end end def denominator if infinite? || nan? 1 else super end end def to_r f, e = Math.frexp self f = Math.ldexp(f, MANT_DIG).to_i e -= MANT_DIG (f * (RADIX ** e)).to_r end def arg if nan? self elsif signbit? Math::PI else 0 end end alias_method :angle, :arg alias_method :phase, :arg def rationalize(eps=undefined) if undefined.equal?(eps) f, n = Math.frexp self f = Math.ldexp(f, Float::MANT_DIG).to_i n -= Float::MANT_DIG Rational.new(2 * f, 1 << (1 - n)).rationalize(Rational.new(1, 1 << (1 - n))) else to_r.rationalize(eps) end end def round(ndigits=0) ndigits = Rubinius::Type.coerce_to(ndigits, Integer, :to_int) if ndigits == 0 return Rubinius.invoke_primitive :float_round, self elsif ndigits < 0 return truncate.round ndigits end return self if infinite? or nan? _, exp = Math.frexp(self) if ndigits >= (Float::DIG + 2) - (exp > 0 ? exp / 4 : exp / 3 - 1) return self end if ndigits < -(exp > 0 ? exp / 3 + 1 : exp / 4) return 0.0 end f = 10**ndigits Rubinius.invoke_primitive(:float_round, self * f) / f.to_f end def coerce(other) return [other, self] if other.kind_of? Float [Float(other), self] end def -@ Rubinius.primitive :float_neg raise PrimitiveFailure, "Float#-@ primitive failed" end def abs FFI::Platform::Math.fabs(self) end alias_method :magnitude, :abs def signbit? Rubinius.primitive :float_signbit_p raise PrimitiveFailure, "Float#signbit? primitive failed" end def +(other) Rubinius.asm do flt = new_label done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_1 r3 n_ieq r3, r3, r2 b_if r3, flt goto done flt.set! n_dadd r0, r0, r1 r_store_float r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end b, a = math_coerce other a + b end def -(other) Rubinius.asm do flt = new_label done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_1 r3 n_ieq r3, r3, r2 b_if r3, flt goto done flt.set! n_dsub r0, r0, r1 r_store_float r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end b, a = math_coerce other a - b end def *(other) Rubinius.asm do flt = new_label done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_1 r3 n_ieq r3, r3, r2 b_if r3, flt goto done flt.set! n_dmul r0, r0, r1 r_store_float r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end b, a = math_coerce other a * b end def /(other) Rubinius.asm do flt = new_label done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_1 r3 n_ieq r3, r3, r2 b_if r3, flt goto done flt.set! n_ddiv r0, r0, r1 r_store_float r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end redo_coerced :/, other end alias_method :divide, :/ alias_method :quo, :/ alias_method :fdiv, :/ INFINITY = 1.0 / 0.0 NAN = 0.0 / 0.0 def divmod(other) Rubinius.primitive :float_divmod b, a = math_coerce other a.divmod b end def %(other) return 0 / 0.to_f if other == 0 Rubinius.primitive :float_mod b, a = math_coerce other a % b end alias_method :modulo, :% def <(other) Rubinius.asm do done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_0 r3 n_ieq r3, r3, r2 b_if r3, done n_dlt r0, r0, r1 r_load_bool r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end b, a = math_coerce other, :compare_error a < b end def <=(other) Rubinius.asm do done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_0 r3 n_ieq r3, r3, r2 b_if r3, done n_dle r0, r0, r1 r_load_bool r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end b, a = math_coerce other, :compare_error a <= b end def >(other) Rubinius.asm do done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_0 r3 n_ieq r3, r3, r2 b_if r3, done n_dgt r0, r0, r1 r_load_bool r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end b, a = math_coerce other, :compare_error a > b end def >=(other) Rubinius.asm do done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_0 r3 n_ieq r3, r3, r2 b_if r3, done n_dge r0, r0, r1 r_load_bool r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end b, a = math_coerce other, :compare_error a >= b end def <=>(other) Rubinius.primitive :float_compare b, a = math_coerce other, :compare_error a <=> b rescue ArgumentError nil end def !=(other) Rubinius.asm do done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_0 r3 n_ieq r3, r3, r2 b_if r3, done n_dne r0, r0, r1 r_load_bool r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end b, a = math_coerce(other) a != b ? true : false end def ==(other) Rubinius.asm do done = new_label r0 = new_register r1 = new_register r2 = new_register r3 = new_register r_load_m_binops r0, r1 n_promote r2, r0, r1 r_load_0 r3 n_ieq r3, r3, r2 b_if r3, done n_deq r0, r0, r1 r_load_bool r0, r0 r_ret r0 done.set! # TODO: teach the bytecode compiler better push_true end begin b, a = math_coerce(other) a == b ? true : false rescue TypeError return other == self end end def eql?(other) Rubinius.primitive :float_eql false end def nan? Rubinius.asm do r0 = new_register r1 = new_register r_load_self r0 r_load_float r0, r0 n_dnan r1, r0 r_load_bool r0, r1 r_ret r0 # TODO: teach the bytecode compiler better push_true end end def infinite? Rubinius.asm do no = new_label r0 = new_register r1 = new_register r2 = new_register r_load_self r0 r_load_float r0, r0 n_dinf r1, r0 r_load_0 r0 n_ieq r2, r1, r0 b_if r2, no r_store_int r0, r1 r_ret r0 no.set! r_load_nil r0, 0 r_ret r0 # TODO: teach the bytecode compiler better push_true end end def finite? not (nan? or infinite?) end def to_f self end def to_i Rubinius.primitive :float_to_i raise PrimitiveFailure, "Float#to_i primitive failed" end alias_method :to_int, :to_i alias_method :truncate, :to_i def to_s to_s_minimal end alias_method :inspect, :to_s def to_s_minimal Rubinius.primitive :float_to_s_minimal raise PrimitiveFailure, "Float#to_s_minimal primitive failed: output exceeds buffer size" end def to_s_formatted(fmt) Rubinius.primitive :float_to_s_formatted raise PrimitiveFailure, "Float#to_s_formatted primitive failed: output exceeds buffer size" end private :to_s_formatted def dtoa Rubinius.primitive :float_dtoa raise PrimitiveFailure, "Fload#dtoa primitive failed" end def to_packed(size) Rubinius.primitive :float_to_packed raise PrimitiveFailure, "Float#to_packed primitive failed" end def ceil int = to_i() return int if self == int or self < 0 return int + 1 end def floor int = to_i() return int if self > 0 or self == int return int - 1 end def next_float return NAN if self.nan? return -MAX if self == -INFINITY return INFINITY if self == MAX return Math.ldexp(0.5, MIN_EXP - MANT_DIG + 1) if self.zero? frac, exp = Math.frexp self if frac == -0.5 frac *= 2 exp -= 1 end smallest_frac = EPSILON / 2 smallest_frac = Math.ldexp(smallest_frac, MIN_EXP - exp) if exp < MIN_EXP result_frac = frac + smallest_frac return -0.0 if result_frac.zero? && frac < 0 return 0.0 if result_frac.zero? && frac > 0 return Math.ldexp result_frac, exp end def prev_float return -(-self).next_float end end