def c_cosh(x, y): if not isfinite(x) or not isfinite(y): if isinf(x) and isfinite(y) and y != 0.: if x > 0: real = copysign(INF, math.cos(y)) imag = copysign(INF, math.sin(y)) else: real = copysign(INF, math.cos(y)) imag = -copysign(INF, math.sin(y)) r = (real, imag) else: r = cosh_special_values[special_type(x)][special_type(y)] # need to raise ValueError if y is +/- infinity and x is not # a NaN if isinf(y) and not isnan(x): raise ValueError("math domain error") return r if fabs(x) > CM_LOG_LARGE_DOUBLE: # deal correctly with cases where cosh(x) overflows but # cosh(z) does not. x_minus_one = x - copysign(1., x) real = math.cos(y) * math.cosh(x_minus_one) * math.e imag = math.sin(y) * math.sinh(x_minus_one) * math.e else: real = math.cos(y) * math.cosh(x) imag = math.sin(y) * math.sinh(x) if isinf(real) or isinf(imag): raise OverflowError("math range error") return real, imag
def c_rect(r, phi): if not isfinite(r) or not isfinite(phi): # if r is +/-infinity and phi is finite but nonzero then # result is (+-INF +-INF i), but we need to compute cos(phi) # and sin(phi) to figure out the signs. if isinf(r) and isfinite(phi) and phi != 0.: if r > 0: real = copysign(INF, math.cos(phi)) imag = copysign(INF, math.sin(phi)) else: real = -copysign(INF, math.cos(phi)) imag = -copysign(INF, math.sin(phi)) z = (real, imag) else: z = rect_special_values[special_type(r)][special_type(phi)] # need to raise ValueError if r is a nonzero number and phi # is infinite if r != 0. and not isnan(r) and isinf(phi): raise ValueError("math domain error") return z real = r * math.cos(phi) imag = r * math.sin(phi) return real, imag
def c_sinh(x, y): # special treatment for sinh(+/-inf + iy) if y is finite and nonzero if not isfinite(x) or not isfinite(y): if isinf(x) and isfinite(y) and y != 0.: if x > 0: real = copysign(INF, math.cos(y)) imag = copysign(INF, math.sin(y)) else: real = -copysign(INF, math.cos(y)) imag = copysign(INF, math.sin(y)) r = (real, imag) else: r = sinh_special_values[special_type(x)][special_type(y)] # need to raise ValueError if y is +/- infinity and x is not # a NaN if isinf(y) and not isnan(x): raise ValueError("math domain error") return r if fabs(x) > CM_LOG_LARGE_DOUBLE: x_minus_one = x - copysign(1., x) real = math.cos(y) * math.sinh(x_minus_one) * math.e imag = math.sin(y) * math.cosh(x_minus_one) * math.e else: real = math.cos(y) * math.sinh(x) imag = math.sin(y) * math.cosh(x) if isinf(real) or isinf(imag): raise OverflowError("math range error") return real, imag
def c_atanh(x, y): if not isfinite(x) or not isfinite(y): return atanh_special_values[special_type(x)][special_type(y)] # Reduce to case where x >= 0., using atanh(z) = -atanh(-z). if x < 0.: return c_neg(*c_atanh(*c_neg(x, y))) ay = fabs(y) if x > CM_SQRT_LARGE_DOUBLE or ay > CM_SQRT_LARGE_DOUBLE: # if abs(z) is large then we use the approximation # atanh(z) ~ 1/z +/- i*pi/2 (+/- depending on the sign # of y h = math.hypot(x/2., y/2.) # safe from overflow real = x/4./h/h # the two negations in the next line cancel each other out # except when working with unsigned zeros: they're there to # ensure that the branch cut has the correct continuity on # systems that don't support signed zeros imag = -copysign(math.pi/2., -y) elif x == 1. and ay < CM_SQRT_DBL_MIN: # C99 standard says: atanh(1+/-0.) should be inf +/- 0i if ay == 0.: raise ValueError("math domain error") #real = INF #imag = y else: real = -math.log(math.sqrt(ay)/math.sqrt(math.hypot(ay, 2.))) imag = copysign(math.atan2(2., -ay) / 2, y) else: real = log1p(4.*x/((1-x)*(1-x) + ay*ay))/4. imag = -math.atan2(-2.*y, (1-x)*(1+x) - ay*ay) / 2. return (real, imag)
def c_exp(x, y): if not isfinite(x) or not isfinite(y): if isinf(x) and isfinite(y) and y != 0.: if x > 0: real = copysign(INF, math.cos(y)) imag = copysign(INF, math.sin(y)) else: real = copysign(0., math.cos(y)) imag = copysign(0., math.sin(y)) r = (real, imag) else: r = exp_special_values[special_type(x)][special_type(y)] # need to raise ValueError if y is +/- infinity and x is not # a NaN and not -infinity if isinf(y) and (isfinite(x) or (isinf(x) and x > 0)): raise ValueError("math domain error") return r if x > CM_LOG_LARGE_DOUBLE: l = math.exp(x-1.) real = l * math.cos(y) * math.e imag = l * math.sin(y) * math.e else: l = math.exp(x) real = l * math.cos(y) imag = l * math.sin(y) if isinf(real) or isinf(imag): raise OverflowError("math range error") return real, imag
def _make_key(self, obj): # see the tests 'test_zeros_not_mixed*' in ../test/test_compiler.py space = self.space w_type = space.type(obj) if space.is_w(w_type, space.w_float): val = space.float_w(obj) if val == 0.0 and rfloat.copysign(1.0, val) < 0: w_key = space.newtuple([obj, space.w_float, space.w_None]) else: w_key = space.newtuple([obj, space.w_float]) elif space.is_w(w_type, space.w_complex): w_real = space.getattr(obj, space.wrap("real")) w_imag = space.getattr(obj, space.wrap("imag")) real = space.float_w(w_real) imag = space.float_w(w_imag) real_negzero = real == 0.0 and rfloat.copysign(1.0, real) < 0 imag_negzero = imag == 0.0 and rfloat.copysign(1.0, imag) < 0 if real_negzero and imag_negzero: tup = [obj, space.w_complex, space.w_None, space.w_None, space.w_None] elif imag_negzero: tup = [obj, space.w_complex, space.w_None, space.w_None] elif real_negzero: tup = [obj, space.w_complex, space.w_None] else: tup = [obj, space.w_complex] w_key = space.newtuple(tup) elif space.is_w(w_type, space.w_tuple): result_w = [obj, w_type] for w_item in space.fixedview(obj): result_w.append(self._make_key(w_item)) w_key = space.newtuple(result_w[:]) else: w_key = space.newtuple([obj, w_type]) return w_key
def test_nan_and_special_values(): from rpython.rlib.rfloat import isnan, isinf, isfinite, copysign inf = 1e300 * 1e300 assert isinf(inf) nan = inf/inf assert isnan(nan) for value, checker in [ (inf, lambda x: isinf(x) and x > 0.0), (-inf, lambda x: isinf(x) and x < 0.0), (nan, isnan), (42.0, isfinite), (0.0, lambda x: not x and copysign(1., x) == 1.), (-0.0, lambda x: not x and copysign(1., x) == -1.), ]: def f(): return value f1 = compile(f, []) res = f1() assert checker(res) l = [value] def g(x): return l[x] g2 = compile(g, [int]) res = g2(0) assert checker(res) l2 = [(-value, -value), (value, value)] def h(x): return l2[x][1] h3 = compile(h, [int]) res = h3(1) assert checker(res)
def descr_str(self, space): if self.realval == 0 and copysign(1., self.realval) == 1.: return space.wrap(str_format(self.imagval) + 'j') sign = (copysign(1., self.imagval) == 1. or isnan(self.imagval)) and '+' or '' return space.wrap('(' + str_format(self.realval) + sign + str_format(self.imagval) + 'j)')
def __eq__(self, other): if (type(self) is SomeFloat and type(other) is SomeFloat and self.is_constant() and other.is_constant()): from rpython.rlib.rfloat import isnan, copysign # NaN unpleasantness. if isnan(self.const) and isnan(other.const): return True # 0.0 vs -0.0 unpleasantness. if not self.const and not other.const: return copysign(1., self.const) == copysign(1., other.const) # return super(SomeFloat, self).__eq__(other)
def c_sqrt(x, y): ''' Method: use symmetries to reduce to the case when x = z.real and y = z.imag are nonnegative. Then the real part of the result is given by s = sqrt((x + hypot(x, y))/2) and the imaginary part is d = (y/2)/s If either x or y is very large then there's a risk of overflow in computation of the expression x + hypot(x, y). We can avoid this by rewriting the formula for s as: s = 2*sqrt(x/8 + hypot(x/8, y/8)) This costs us two extra multiplications/divisions, but avoids the overhead of checking for x and y large. If both x and y are subnormal then hypot(x, y) may also be subnormal, so will lack full precision. We solve this by rescaling x and y by a sufficiently large power of 2 to ensure that x and y are normal. ''' if not isfinite(x) or not isfinite(y): return sqrt_special_values[special_type(x)][special_type(y)] if x == 0. and y == 0.: return (0., y) ax = fabs(x) ay = fabs(y) if ax < DBL_MIN and ay < DBL_MIN and (ax > 0. or ay > 0.): # here we catch cases where hypot(ax, ay) is subnormal ax = math.ldexp(ax, CM_SCALE_UP) ay1= math.ldexp(ay, CM_SCALE_UP) s = math.ldexp(math.sqrt(ax + math.hypot(ax, ay1)), CM_SCALE_DOWN) else: ax /= 8. s = 2.*math.sqrt(ax + math.hypot(ax, ay/8.)) d = ay/(2.*s) if x >= 0.: return (s, copysign(d, y)) else: return (d, copysign(s, y))
def rAlmostEqual(a, b, rel_err=2e-15, abs_err=5e-323, msg=''): """Fail if the two floating-point numbers are not almost equal. Determine whether floating-point values a and b are equal to within a (small) rounding error. The default values for rel_err and abs_err are chosen to be suitable for platforms where a float is represented by an IEEE 754 double. They allow an error of between 9 and 19 ulps. """ # special values testing if isnan(a): if isnan(b): return True,'' raise AssertionError(msg + '%r should be nan' % (b,)) if isinf(a): if a == b: return True,'' raise AssertionError(msg + 'finite result where infinity expected: '+ \ 'expected %r, got %r' % (a, b)) # if both a and b are zero, check whether they have the same sign # (in theory there are examples where it would be legitimate for a # and b to have opposite signs; in practice these hardly ever # occur). if not a and not b: # only check it if we are running on top of CPython >= 2.6 if sys.version_info >= (2, 6) and copysign(1., a) != copysign(1., b): raise AssertionError( msg + \ 'zero has wrong sign: expected %r, got %r' % (a, b)) # if a-b overflows, or b is infinite, return False. Again, in # theory there are examples where a is within a few ulps of the # max representable float, and then b could legitimately be # infinite. In practice these examples are rare. try: absolute_error = abs(b-a) except OverflowError: pass else: # test passes if either the absolute error or the relative # error is sufficiently small. The defaults amount to an # error of between 9 ulps and 19 ulps on an IEEE-754 compliant # machine. if absolute_error <= max(abs_err, rel_err * abs(a)): return True,'' raise AssertionError(msg + \ '%r and %r are not sufficiently close, %g > %g' %\ (a, b, absolute_error, max(abs_err, rel_err*abs(a))))
def rAlmostEqual(a, b, rel_err=2e-15, abs_err=5e-323, msg=''): """Fail if the two floating-point numbers are not almost equal. Determine whether floating-point values a and b are equal to within a (small) rounding error. The default values for rel_err and abs_err are chosen to be suitable for platforms where a float is represented by an IEEE 754 double. They allow an error of between 9 and 19 ulps. """ # special values testing if isnan(a): if isnan(b): return True, '' raise AssertionError(msg + '%r should be nan' % (b, )) if isinf(a): if a == b: return True, '' raise AssertionError(msg + 'finite result where infinity expected: '+ \ 'expected %r, got %r' % (a, b)) # if both a and b are zero, check whether they have the same sign # (in theory there are examples where it would be legitimate for a # and b to have opposite signs; in practice these hardly ever # occur). if not a and not b: # only check it if we are running on top of CPython >= 2.6 if sys.version_info >= (2, 6) and copysign(1., a) != copysign(1., b): raise AssertionError( msg + \ 'zero has wrong sign: expected %r, got %r' % (a, b)) # if a-b overflows, or b is infinite, return False. Again, in # theory there are examples where a is within a few ulps of the # max representable float, and then b could legitimately be # infinite. In practice these examples are rare. try: absolute_error = abs(b - a) except OverflowError: pass else: # test passes if either the absolute error or the relative # error is sufficiently small. The defaults amount to an # error of between 9 ulps and 19 ulps on an IEEE-754 compliant # machine. if absolute_error <= max(abs_err, rel_err * abs(a)): return True, '' raise AssertionError(msg + \ '%r and %r are not sufficiently close, %g > %g' %\ (a, b, absolute_error, max(abs_err, rel_err*abs(a))))
def c_sqrt(x, y): ''' Method: use symmetries to reduce to the case when x = z.real and y = z.imag are nonnegative. Then the real part of the result is given by s = sqrt((x + hypot(x, y))/2) and the imaginary part is d = (y/2)/s If either x or y is very large then there's a risk of overflow in computation of the expression x + hypot(x, y). We can avoid this by rewriting the formula for s as: s = 2*sqrt(x/8 + hypot(x/8, y/8)) This costs us two extra multiplications/divisions, but avoids the overhead of checking for x and y large. If both x and y are subnormal then hypot(x, y) may also be subnormal, so will lack full precision. We solve this by rescaling x and y by a sufficiently large power of 2 to ensure that x and y are normal. ''' if not isfinite(x) or not isfinite(y): return sqrt_special_values[special_type(x)][special_type(y)] if x == 0. and y == 0.: return (0., y) ax = fabs(x) ay = fabs(y) if ax < DBL_MIN and ay < DBL_MIN and (ax > 0. or ay > 0.): # here we catch cases where hypot(ax, ay) is subnormal ax = math.ldexp(ax, CM_SCALE_UP) ay1 = math.ldexp(ay, CM_SCALE_UP) s = math.ldexp(math.sqrt(ax + math.hypot(ax, ay1)), CM_SCALE_DOWN) else: ax /= 8. s = 2. * math.sqrt(ax + math.hypot(ax, ay / 8.)) d = ay / (2. * s) if x >= 0.: return (s, copysign(d, y)) else: return (d, copysign(s, y))
def float_pack80(x, size): """Convert a Python float or longfloat x into two 64-bit unsigned integers with 80 bit extended representation.""" x = float(x) # longfloat not really supported if size == 10 or size == 12 or size == 16: MIN_EXP = -16381 MAX_EXP = 16384 MANT_DIG = 64 BITS = 80 else: raise ValueError("invalid size value") sign = rfloat.copysign(1.0, x) < 0.0 if rfloat.isinf(x): mant = r_ulonglong(0) exp = MAX_EXP - MIN_EXP + 2 elif rfloat.isnan(x): # rfloat.isnan(x): asint = cast(ULONGLONG, float2longlong(x)) mant = asint & ((r_ulonglong(1) << 51) - 1) if mant == 0: mant = r_ulonglong(1) << (MANT_DIG - 1) - 1 sign = asint < 0 exp = MAX_EXP - MIN_EXP + 2 elif x == 0.0: mant = r_ulonglong(0) exp = 0 else: m, e = math.frexp(abs(x)) # abs(x) == m * 2**e exp = e - (MIN_EXP - 1) if exp > 0: # Normal case. Avoid uint64 overflow by using MANT_DIG-1 mant = round_to_nearest(m * (r_ulonglong(1) << MANT_DIG - 1)) else: # Subnormal case. if exp + MANT_DIG - 1 >= 0: mant = round_to_nearest(m * (r_ulonglong(1) << exp + MANT_DIG - 1)) else: mant = r_ulonglong(0) exp = 0 # Special case: rounding produced a MANT_DIG-bit mantissa. if mant == r_ulonglong(1) << MANT_DIG - 1: mant = r_ulonglong(0) exp += 1 # Raise on overflow (in some circumstances, may want to return # infinity instead). if exp >= MAX_EXP - MIN_EXP + 2: raise OverflowError("float too large to pack in this format") mant = mant << 1 # check constraints if not objectmodel.we_are_translated(): assert 0 <= mant <= (1 << MANT_DIG) - 1 assert 0 <= exp <= MAX_EXP - MIN_EXP + 2 assert 0 <= sign <= 1 exp = r_ulonglong(exp) sign = r_ulonglong(sign) return (mant, (sign << BITS - MANT_DIG - 1) | exp)
def descr_hex(self, space): TOHEX_NBITS = rfloat.DBL_MANT_DIG + 3 - (rfloat.DBL_MANT_DIG + 2) % 4 value = self.floatval if not isfinite(value): return self.descr_str(space) if value == 0.0: if copysign(1., value) == -1.: return space.wrap("-0x0.0p+0") else: return space.wrap("0x0.0p+0") mant, exp = math.frexp(value) shift = 1 - max(rfloat.DBL_MIN_EXP - exp, 0) mant = math.ldexp(mant, shift) mant = abs(mant) exp -= shift result = ['\0'] * ((TOHEX_NBITS - 1) // 4 + 2) result[0] = _char_from_hex(int(mant)) mant -= int(mant) result[1] = "." for i in range((TOHEX_NBITS - 1) // 4): mant *= 16.0 result[i + 2] = _char_from_hex(int(mant)) mant -= int(mant) if exp < 0: sign = "-" else: sign = "+" exp = abs(exp) s = ''.join(result) if value < 0.0: return space.wrap("-0x%sp%s%d" % (s, sign, exp)) else: return space.wrap("0x%sp%s%d" % (s, sign, exp))
def test_special_values(): from rpython.rlib.special_value import sqrt_special_values assert len(sqrt_special_values) == 7 assert len(sqrt_special_values[4]) == 7 assert isinstance(sqrt_special_values[5][1], tuple) assert sqrt_special_values[5][1][0] == 1e200 * 1e200 assert sqrt_special_values[5][1][1] == -0. assert copysign(1., sqrt_special_values[5][1][1]) == -1.
def float_mod(a, b): y = b.number mod = math_fmod(a.number, y) # Follows pypy implementation. if mod: # I'm not sure why remainder and denominator if (y < 0.0) != (mod < 0.0): # must have the same sign. mod += y else: mod = copysign(0.0, y) return Float(mod)
def c_asinh(x, y): if not isfinite(x) or not isfinite(y): return asinh_special_values[special_type(x)][special_type(y)] if fabs(x) > CM_LARGE_DOUBLE or fabs(y) > CM_LARGE_DOUBLE: if y >= 0.: real = copysign(math.log(math.hypot(x/2., y/2.)) + M_LN2*2., x) else: real = -copysign(math.log(math.hypot(x/2., y/2.)) + M_LN2*2., -x) imag = math.atan2(y, fabs(x)) else: s1x, s1y = c_sqrt(1.+y, -x) s2x, s2y = c_sqrt(1.-y, x) real = asinh(s1x*s2y - s2x*s1y) imag = math.atan2(y, s1x*s2x - s1y*s2y) return (real, imag)
def c_asinh(x, y): if not isfinite(x) or not isfinite(y): return asinh_special_values[special_type(x)][special_type(y)] if fabs(x) > CM_LARGE_DOUBLE or fabs(y) > CM_LARGE_DOUBLE: if y >= 0.: real = copysign( math.log(math.hypot(x / 2., y / 2.)) + M_LN2 * 2., x) else: real = -copysign( math.log(math.hypot(x / 2., y / 2.)) + M_LN2 * 2., -x) imag = math.atan2(y, fabs(x)) else: s1x, s1y = c_sqrt(1. + y, -x) s2x, s2y = c_sqrt(1. - y, x) real = asinh(s1x * s2y - s2x * s1y) imag = math.atan2(y, s1x * s2x - s1y * s2y) return (real, imag)
def method_atanh(self, space, value): try: res = math.atanh(value) except ValueError: if value == 1.0 or value == -1.0: # produce an infinity with the right sign res = rfloat.copysign(rfloat.INFINITY, value) else: raise space.error(space.getclassfor(W_DomainError), 'Numerical argument is out of domain - "atanh"') return space.newfloat(res)
def c_acos(x, y): if not isfinite(x) or not isfinite(y): return acos_special_values[special_type(x)][special_type(y)] if fabs(x) > CM_LARGE_DOUBLE or fabs(y) > CM_LARGE_DOUBLE: # avoid unnecessary overflow for large arguments real = math.atan2(fabs(y), x) # split into cases to make sure that the branch cut has the # correct continuity on systems with unsigned zeros if x < 0.: imag = -copysign(math.log(math.hypot(x/2., y/2.)) + M_LN2*2., y) else: imag = copysign(math.log(math.hypot(x/2., y/2.)) + M_LN2*2., -y) else: s1x, s1y = c_sqrt(1.-x, -y) s2x, s2y = c_sqrt(1.+x, y) real = 2.*math.atan2(s1x, s2x) imag = asinh(s2x*s1y - s2y*s1x) return (real, imag)
def method_fdiv(self, space, w_other): if space.is_kind_of(w_other, space.w_fixnum): other = space.int_w(w_other) try: res = float(self.intvalue) / float(other) except ZeroDivisionError: return space.newfloat(rfloat.copysign(rfloat.INFINITY, float(self.intvalue))) else: return space.newfloat(res) elif space.is_kind_of(w_other, space.w_bignum): return space.send(space.newbigint_fromint(self.intvalue), "fdiv", [w_other]) elif space.is_kind_of(w_other, space.w_float): other = space.float_w(w_other) try: res = float(self.intvalue) / other except ZeroDivisionError: return space.newfloat(rfloat.copysign(rfloat.INFINITY, float(self.intvalue))) else: return space.newfloat(res) else: return W_NumericObject.retry_binop_coercing(space, self, w_other, "fdiv")
def method_atanh(self, space, value): try: res = math.atanh(value) except ValueError: if value == 1.0 or value == -1.0: # produce an infinity with the right sign res = rfloat.copysign(rfloat.INFINITY, value) else: raise space.error( space.getclassfor(W_DomainError), 'Numerical argument is out of domain - "atanh"') return space.newfloat(res)
def c_acos(x, y): if not isfinite(x) or not isfinite(y): return acos_special_values[special_type(x)][special_type(y)] if fabs(x) > CM_LARGE_DOUBLE or fabs(y) > CM_LARGE_DOUBLE: # avoid unnecessary overflow for large arguments real = math.atan2(fabs(y), x) # split into cases to make sure that the branch cut has the # correct continuity on systems with unsigned zeros if x < 0.: imag = -copysign( math.log(math.hypot(x / 2., y / 2.)) + M_LN2 * 2., y) else: imag = copysign( math.log(math.hypot(x / 2., y / 2.)) + M_LN2 * 2., -y) else: s1x, s1y = c_sqrt(1. - x, -y) s2x, s2y = c_sqrt(1. + x, y) real = 2. * math.atan2(s1x, s2x) imag = asinh(s2x * s1y - s2y * s1x) return (real, imag)
def method_gamma(self, space, value): try: res = rfloat.gamma(value) except ValueError: if value == 0.0: # produce an infinity with the right sign res = rfloat.copysign(rfloat.INFINITY, value) else: raise space.error(space.getclassfor(W_DomainError), 'Numerical argument is out of domain - "gamma"') except OverflowError: res = rfloat.INFINITY return space.newfloat(res)
def c_tanh(x, y): # Formula: # # tanh(x+iy) = (tanh(x)(1+tan(y)^2) + i tan(y)(1-tanh(x))^2) / # (1+tan(y)^2 tanh(x)^2) # # To avoid excessive roundoff error, 1-tanh(x)^2 is better computed # as 1/cosh(x)^2. When abs(x) is large, we approximate 1-tanh(x)^2 # by 4 exp(-2*x) instead, to avoid possible overflow in the # computation of cosh(x). if not isfinite(x) or not isfinite(y): if isinf(x) and isfinite(y) and y != 0.: if x > 0: real = 1.0 # vv XXX why is the 2. there? imag = copysign(0., 2. * math.sin(y) * math.cos(y)) else: real = -1.0 imag = copysign(0., 2. * math.sin(y) * math.cos(y)) r = (real, imag) else: r = tanh_special_values[special_type(x)][special_type(y)] # need to raise ValueError if y is +/-infinity and x is finite if isinf(y) and isfinite(x): raise ValueError("math domain error") return r if fabs(x) > CM_LOG_LARGE_DOUBLE: real = copysign(1., x) imag = 4. * math.sin(y) * math.cos(y) * math.exp(-2.*fabs(x)) else: tx = math.tanh(x) ty = math.tan(y) cx = 1. / math.cosh(x) txty = tx * ty denom = 1. + txty * txty real = tx * (1. + ty*ty) / denom imag = ((ty / denom) * cx) * cx return real, imag
def test_nan_and_special_values(): from rpython.rlib.rfloat import isnan, isinf, isfinite, copysign inf = 1e300 * 1e300 assert isinf(inf) nan = inf / inf assert isnan(nan) for value, checker in [ (inf, lambda x: isinf(x) and x > 0.0), (-inf, lambda x: isinf(x) and x < 0.0), (nan, isnan), (42.0, isfinite), (0.0, lambda x: not x and copysign(1., x) == 1.), (-0.0, lambda x: not x and copysign(1., x) == -1.), ]: def f(): return value f1 = compile(f, []) res = f1() assert checker(res) l = [value] def g(x): return l[x] g2 = compile(g, [int]) res = g2(0) assert checker(res) l2 = [(-value, -value), (value, value)] def h(x): return l2[x][1] h3 = compile(h, [int]) res = h3(1) assert checker(res)
def c_tanh(x, y): # Formula: # # tanh(x+iy) = (tanh(x)(1+tan(y)^2) + i tan(y)(1-tanh(x))^2) / # (1+tan(y)^2 tanh(x)^2) # # To avoid excessive roundoff error, 1-tanh(x)^2 is better computed # as 1/cosh(x)^2. When abs(x) is large, we approximate 1-tanh(x)^2 # by 4 exp(-2*x) instead, to avoid possible overflow in the # computation of cosh(x). if not isfinite(x) or not isfinite(y): if isinf(x) and isfinite(y) and y != 0.: if x > 0: real = 1.0 # vv XXX why is the 2. there? imag = copysign(0., 2. * math.sin(y) * math.cos(y)) else: real = -1.0 imag = copysign(0., 2. * math.sin(y) * math.cos(y)) r = (real, imag) else: r = tanh_special_values[special_type(x)][special_type(y)] # need to raise ValueError if y is +/-infinity and x is finite if isinf(y) and isfinite(x): raise ValueError("math domain error") return r if fabs(x) > CM_LOG_LARGE_DOUBLE: real = copysign(1., x) imag = 4. * math.sin(y) * math.cos(y) * math.exp(-2. * fabs(x)) else: tx = math.tanh(x) ty = math.tan(y) cx = 1. / math.cosh(x) txty = tx * ty denom = 1. + txty * txty real = tx * (1. + ty * ty) / denom imag = ((ty / denom) * cx) * cx return real, imag
def _make_key(self, obj): # see the tests 'test_zeros_not_mixed*' in ../test/test_compiler.py space = self.space w_type = space.type(obj) if space.is_w(w_type, space.w_float): val = space.float_w(obj) if val == 0.0 and rfloat.copysign(1., val) < 0: w_key = space.newtuple([obj, space.w_float, space.w_None]) else: w_key = space.newtuple([obj, space.w_float]) elif space.is_w(w_type, space.w_complex): w_real = space.getattr(obj, space.newtext("real")) w_imag = space.getattr(obj, space.newtext("imag")) real = space.float_w(w_real) imag = space.float_w(w_imag) real_negzero = (real == 0.0 and rfloat.copysign(1., real) < 0) imag_negzero = (imag == 0.0 and rfloat.copysign(1., imag) < 0) if real_negzero and imag_negzero: tup = [ obj, space.w_complex, space.w_None, space.w_None, space.w_None ] elif imag_negzero: tup = [obj, space.w_complex, space.w_None, space.w_None] elif real_negzero: tup = [obj, space.w_complex, space.w_None] else: tup = [obj, space.w_complex] w_key = space.newtuple(tup) elif space.is_w(w_type, space.w_tuple): result_w = [obj, w_type] for w_item in space.fixedview(obj): result_w.append(self._make_key(w_item)) w_key = space.newtuple(result_w[:]) elif isinstance(obj, PyCode): w_key = space.newtuple([obj, w_type, space.id(obj)]) else: w_key = space.newtuple([obj, w_type]) return w_key
def method_gamma(self, space, value): try: res = rfloat.gamma(value) except ValueError: if value == 0.0: # produce an infinity with the right sign res = rfloat.copysign(rfloat.INFINITY, value) else: raise space.error( space.getclassfor(W_DomainError), 'Numerical argument is out of domain - "gamma"') except OverflowError: res = rfloat.INFINITY return space.newfloat(res)
def __init__(self, value): self.value = value # a concrete value # try to be smart about constant mutable or immutable values key = type(self.value), self.value # to avoid confusing e.g. 0 and 0.0 # # we also have to avoid confusing 0.0 and -0.0 (needed e.g. for # translating the cmath module) if key[0] is float and not self.value: from rpython.rlib.rfloat import copysign if copysign(1., self.value) == -1.: # -0.0 key = (float, "-0.0") # try: hash(key) except TypeError: key = id(self.value) self.key = key
def special_type(d): if isnan(d): return ST_NAN elif isinf(d): if d > 0.0: return ST_PINF else: return ST_NINF else: if d != 0.0: if d > 0.0: return ST_POS else: return ST_NEG else: if copysign(1., d) == 1.: return ST_PZERO else: return ST_NZERO
def test_power_complex(self): inf = float('inf') ninf = -float('inf') nan = float('nan') cmpl = complex from math import copysign from numpypy import power, array, complex128, complex64 # note: in some settings (namely a x86-32 build without the JIT), # gcc optimizes the code in rlib.rcomplex.c_pow() to not truncate # the 10-byte values down to 8-byte values. It ends up with more # imprecision than usual (hence 2e-13 instead of 2e-15). for c,rel_err in ((complex128, 2e-13), (complex64, 4e-7)): a = array([cmpl(-5., 0), cmpl(-5., -5.), cmpl(-5., 5.), cmpl(0., -5.), cmpl(0., 0.), cmpl(0., 5.), cmpl(-0., -5.), cmpl(-0., 0.), cmpl(-0., 5.), cmpl(-0., -0.), cmpl(inf, 0.), cmpl(inf, 5.), cmpl(inf, -0.), cmpl(ninf, 0.), cmpl(ninf, 5.), cmpl(ninf, -0.), cmpl(ninf, inf), cmpl(inf, inf), cmpl(ninf, ninf), cmpl(5., inf), cmpl(5., ninf), cmpl(nan, 5.), cmpl(5., nan), cmpl(nan, nan), ], dtype=c) for p in (3, -1, 10000, 2.3, -10000, 10+3j): b = power(a, p) for i in range(len(a)): try: r = self.c_pow((float(a[i].real), float(a[i].imag)), (float(p.real), float(p.imag))) except ZeroDivisionError: r = (nan, nan) except OverflowError: r = (inf, -copysign(inf, a[i].imag)) except ValueError: r = (nan, nan) msg = 'result of %r(%r)**%r got %r expected %r\n ' % \ (c,a[i], p, b[i], r) t1 = float(r[0]) t2 = float(b[i].real) self.rAlmostEqual(t1, t2, rel_err=rel_err, msg=msg) t1 = float(r[1]) t2 = float(b[i].imag) self.rAlmostEqual(t1, t2, rel_err=rel_err, msg=msg)
def descr_mod(self, space, w_rhs): w_rhs = self._to_float(space, w_rhs) if w_rhs is None: return space.w_NotImplemented x = self.floatval y = w_rhs.floatval if y == 0.0: raise oefmt(space.w_ZeroDivisionError, "float modulo") mod = math_fmod(x, y) if mod: # ensure the remainder has the same sign as the denominator if (y < 0.0) != (mod < 0.0): mod += y else: # the remainder is zero, and in the presence of signed zeroes # fmod returns different results across platforms; ensure # it has the same sign as the denominator; we'd like to do # "mod = y * 0.0", but that may get optimized away mod = copysign(0.0, y) return W_FloatObject(mod)
def pack_float(space, packer, repetitions): if repetitions > len(packer.args_w) - packer.args_index: raise space.error(space.w_ArgumentError, "too few arguments") for i in xrange(packer.args_index, repetitions + packer.args_index): w_item = packer.args_w[i] if not (isinstance(w_item, W_FloatObject) or isinstance(w_item, W_FixnumObject)): raise space.error(space.w_TypeError, "can't convert %s into Float" % space.obj_to_s(space.getclass(w_item)) ) doubleval = space.float_w(w_item) l = ["\0"] * size try: unsigned = float_pack(doubleval, size) except OverflowError: unsigned = float_pack(rfloat.copysign(rfloat.INFINITY, doubleval), size) for i in xrange(size): l[i] = chr((unsigned >> (i * 8)) & 0xff) if bigendian: l.reverse() packer.result.extend(l) packer.args_index += repetitions
def mod__Float_Float(space, w_float1, w_float2): x = w_float1.floatval y = w_float2.floatval if y == 0.0: raise FailedToImplementArgs(space.w_ZeroDivisionError, space.wrap("float modulo")) try: mod = math.fmod(x, y) except ValueError: mod = rfloat.NAN else: if mod: # ensure the remainder has the same sign as the denominator if (y < 0.0) != (mod < 0.0): mod += y else: # the remainder is zero, and in the presence of signed zeroes # fmod returns different results across platforms; ensure # it has the same sign as the denominator; we'd like to do # "mod = y * 0.0", but that may get optimized away mod = copysign(0.0, y) return W_FloatObject(mod)
def pack_float(space, packer, repetitions): if repetitions > len(packer.args_w) - packer.args_index: raise space.error(space.w_ArgumentError, "too few arguments") for i in xrange(packer.args_index, repetitions + packer.args_index): w_item = packer.args_w[i] if not (isinstance(w_item, W_FloatObject) or isinstance(w_item, W_FixnumObject)): raise space.error( space.w_TypeError, "can't convert %s into Float" % space.obj_to_s(space.getclass(w_item))) doubleval = space.float_w(w_item) l = ["\0"] * size try: unsigned = float_pack(doubleval, size) except OverflowError: unsigned = float_pack( rfloat.copysign(rfloat.INFINITY, doubleval), size) for i in xrange(size): l[i] = chr((unsigned >> (i * 8)) & 0xff) if bigendian: l.reverse() packer.result.extend(l) packer.args_index += repetitions
def c_phase(x, y): # Windows screws up atan2 for inf and nan, and alpha Tru64 5.1 doesn't # follow C99 for atan2(0., 0.). if isnan(x) or isnan(y): return NAN if isinf(y): if isinf(x): if copysign(1., x) == 1.: # atan2(+-inf, +inf) == +-pi/4 return copysign(0.25 * math.pi, y) else: # atan2(+-inf, -inf) == +-pi*3/4 return copysign(0.75 * math.pi, y) # atan2(+-inf, x) == +-pi/2 for finite x return copysign(0.5 * math.pi, y) if isinf(x) or y == 0.: if copysign(1., x) == 1.: # atan2(+-y, +inf) = atan2(+-0, +x) = +-0. return copysign(0., y) else: # atan2(+-y, -inf) = atan2(+-0., -x) = +-pi. return copysign(math.pi, y) return math.atan2(y, x)
def _pow(space, x, y): # Sort out special cases here instead of relying on pow() if y == 2.0: # special case for performance: return x * x # x * x is always correct if y == 0.0: # x**0 is 1, even 0**0 return 1.0 if isnan(x): # nan**y = nan, unless y == 0 return x if isnan(y): # x**nan = nan, unless x == 1; x**nan = x if x == 1.0: return 1.0 else: return y if isinf(y): # x**inf is: 0.0 if abs(x) < 1; 1.0 if abs(x) == 1; inf if # abs(x) > 1 (including case where x infinite) # # x**-inf is: inf if abs(x) < 1; 1.0 if abs(x) == 1; 0.0 if # abs(x) > 1 (including case where v infinite) x = abs(x) if x == 1.0: return 1.0 elif (y > 0.0) == (x > 1.0): return INFINITY else: return 0.0 if isinf(x): # (+-inf)**w is: inf for w positive, 0 for w negative; in oth # cases, we need to add the appropriate sign if w is an odd # integer. y_is_odd = math.fmod(abs(y), 2.0) == 1.0 if y > 0.0: if y_is_odd: return x else: return abs(x) else: if y_is_odd: return copysign(0.0, x) else: return 0.0 if x == 0.0: if y < 0.0: raise oefmt(space.w_ZeroDivisionError, "0.0 cannot be raised to a negative power") negate_result = False # special case: "(-1.0) ** bignum" should not raise PowDomainError, # unlike "math.pow(-1.0, bignum)". See http://mail.python.org/ # - pipermail/python-bugs-list/2003-March/016795.html if x < 0.0: if isnan(y): return NAN if math.floor(y) != y: raise PowDomainError # y is an exact integer, albeit perhaps a very large one. # Replace x by its absolute value and remember to negate the # pow result if y is odd. x = -x negate_result = math.fmod(abs(y), 2.0) == 1.0 if x == 1.0: # (-1) ** large_integer also ends up here if negate_result: return -1.0 else: return 1.0 try: # We delegate to our implementation of math.pow() the error detection. z = math.pow(x, y) except OverflowError: raise oefmt(space.w_OverflowError, "float power") except ValueError: raise oefmt(space.w_ValueError, "float power") if negate_result: z = -z return z
def test_copysign(): assert copysign(1, 1) == 1 assert copysign(-1, 1) == 1 assert copysign(-1, -1) == -1 assert copysign(1, -1) == -1 assert copysign(1, -0.) == -1
def copysign(space, w_x, w_y): """Return x with the sign of y.""" # No exceptions possible. x = _get_double(space, w_x) y = _get_double(space, w_y) return space.wrap(rfloat.copysign(x, y))
def _format_complex(self, w_complex): flags = 0 space = self.space tp = self._type self._get_locale(tp) default_precision = 6 if self._align == "=": # '=' alignment is invalid msg = ("'=' alignment flag is not allowed in" " complex format specifier") raise OperationError(space.w_ValueError, space.wrap(msg)) if self._fill_char == "0": #zero padding is invalid msg = "Zero padding is not allowed in complex format specifier" raise OperationError(space.w_ValueError, space.wrap(msg)) if self._alternate: flags |= rfloat.DTSF_ALT skip_re = 0 add_parens = 0 if tp == "\0": #should mirror str() output tp = "g" default_precision = 12 #test if real part is non-zero if (w_complex.realval == 0 and copysign(1., w_complex.realval) == 1.): skip_re = 1 else: add_parens = 1 if tp == "n": #same as 'g' except for locale, taken care of later tp = "g" #check if precision not set if self._precision == -1: self._precision = default_precision #in CPython it's named 're' - clashes with re module re_num, special = rfloat.double_to_string(w_complex.realval, tp, self._precision, flags) im_num, special = rfloat.double_to_string(w_complex.imagval, tp, self._precision, flags) n_re_digits = len(re_num) n_im_digits = len(im_num) to_real_number = 0 to_imag_number = 0 re_sign = im_sign = '' #if a sign character is in the output, remember it and skip if re_num[0] == "-": re_sign = "-" to_real_number = 1 n_re_digits -= 1 if im_num[0] == "-": im_sign = "-" to_imag_number = 1 n_im_digits -= 1 #turn off padding - do it after number composition #calc_num_width uses self._width, so assign to temporary variable, #calculate width of real and imag parts, then reassign padding, align tmp_fill_char = self._fill_char tmp_align = self._align tmp_width = self._width self._fill_char = "\0" self._align = "<" self._width = -1 #determine if we have remainder, might include dec or exponent or both re_have_dec, re_remainder_ptr = self._parse_number( re_num, to_real_number) im_have_dec, im_remainder_ptr = self._parse_number( im_num, to_imag_number) if self.is_unicode: re_num = re_num.decode("latin-1") im_num = im_num.decode("latin-1") #set remainder, in CPython _parse_number sets this #using n_re_digits causes tests to fail re_n_remainder = len(re_num) - re_remainder_ptr im_n_remainder = len(im_num) - im_remainder_ptr re_spec = self._calc_num_width(0, re_sign, to_real_number, n_re_digits, re_n_remainder, re_have_dec, re_num) #capture grouped digits b/c _fill_number reads from self._grouped_digits #self._grouped_digits will get overwritten in imaginary calc_num_width re_grouped_digits = self._grouped_digits if not skip_re: self._sign = "+" im_spec = self._calc_num_width(0, im_sign, to_imag_number, n_im_digits, im_n_remainder, im_have_dec, im_num) im_grouped_digits = self._grouped_digits if skip_re: re_spec.n_total = 0 #reassign width, alignment, fill character self._align = tmp_align self._width = tmp_width self._fill_char = tmp_fill_char #compute L and R padding - stored in self._left_pad and self._right_pad self._calc_padding( self.empty, re_spec.n_total + im_spec.n_total + 1 + add_parens * 2) out = self._builder() fill = self._fill_char #compose the string #add left padding out.append_multiple_char(fill, self._left_pad) if add_parens: out.append(self._lit('(')[0]) #if the no. has a real component, add it if not skip_re: out.append( self._fill_number(re_spec, re_num, to_real_number, 0, fill, re_remainder_ptr, False, re_grouped_digits)) #add imaginary component out.append( self._fill_number(im_spec, im_num, to_imag_number, 0, fill, im_remainder_ptr, False, im_grouped_digits)) #add 'j' character out.append(self._lit('j')[0]) if add_parens: out.append(self._lit(')')[0]) #add right padding out.append_multiple_char(fill, self._right_pad) return self.space.wrap(out.build())
def fn(x, y): return rfloat.copysign(x, y)
def float_pack(x, size): """Convert a Python float x into a 64-bit unsigned integer with the same byte representation.""" if size == 8: MIN_EXP = -1021 # = sys.float_info.min_exp MAX_EXP = 1024 # = sys.float_info.max_exp MANT_DIG = 53 # = sys.float_info.mant_dig BITS = 64 elif size == 4: MIN_EXP = -125 # C's FLT_MIN_EXP MAX_EXP = 128 # FLT_MAX_EXP MANT_DIG = 24 # FLT_MANT_DIG BITS = 32 elif size == 2: MIN_EXP = -13 MAX_EXP = 16 MANT_DIG = 11 BITS = 16 else: raise ValueError("invalid size value") sign = rfloat.copysign(1.0, x) < 0.0 if rfloat.isinf(x): mant = r_ulonglong(0) exp = MAX_EXP - MIN_EXP + 2 elif rfloat.isnan(x): asint = cast(ULONGLONG, float2longlong(x)) sign = asint >> 63 # shift off lower bits, perhaps losing data mant = asint & ((r_ulonglong(1) << 52) - 1) if MANT_DIG < 53: mant = mant >> (53 - MANT_DIG) if mant == 0: mant = r_ulonglong(1) << (MANT_DIG - 1) - 1 exp = MAX_EXP - MIN_EXP + 2 elif x == 0.0: mant = r_ulonglong(0) exp = 0 else: m, e = math.frexp(abs(x)) # abs(x) == m * 2**e exp = e - (MIN_EXP - 1) if exp > 0: # Normal case. mant = round_to_nearest(m * (r_ulonglong(1) << MANT_DIG)) mant -= r_ulonglong(1) << MANT_DIG - 1 else: # Subnormal case. if exp + MANT_DIG - 1 >= 0: mant = round_to_nearest(m * (r_ulonglong(1) << exp + MANT_DIG - 1)) else: mant = r_ulonglong(0) exp = 0 # Special case: rounding produced a MANT_DIG-bit mantissa. if not objectmodel.we_are_translated(): assert 0 <= mant <= 1 << MANT_DIG - 1 if mant == r_ulonglong(1) << MANT_DIG - 1: mant = r_ulonglong(0) exp += 1 # Raise on overflow (in some circumstances, may want to return # infinity instead). if exp >= MAX_EXP - MIN_EXP + 2: raise OverflowError("float too large to pack in this format") # check constraints if not objectmodel.we_are_translated(): assert 0 <= mant <= (1 << MANT_DIG) - 1 assert 0 <= exp <= MAX_EXP - MIN_EXP + 2 assert 0 <= sign <= 1 exp = r_ulonglong(exp) sign = r_ulonglong(sign) return ((sign << BITS - 1) | (exp << MANT_DIG - 1)) | mant
def method_sinh(self, space, value): try: res = math.sinh(value) except OverflowError: res = rfloat.copysign(rfloat.INFINITY, value) return space.newfloat(res)
(U, U), (Z, -Z), (Z, Z), (U, U), (Z, Z), (Z, Z), (N, N), (U, U), (U, U), (U, U), (U, U), (N, N), (N, N), (INF, N), (U, U), (INF, -Z), (INF, Z), (U, U), (INF, N), (INF, N), (N, N), (N, N), (N, Z), (N, Z), (N, N), (N, N), (N, N), ]) assert copysign(1., acosh_special_values[5][2][1]) == -1.
def method_cosh(self, space, value): try: res = math.cosh(value) except OverflowError: res = rfloat.copysign(rfloat.INFINITY, value) return space.newfloat(res)
def f(x): x = rfloat.copysign(0.0, x) return rfloat.copysign(1.0, rfloat.log1p(x))