# Copyright (C) 2022-2025 Exaloop Inc. @pure @llvm def _inf() -> float: ret double 0x7FF0000000000000 @pure @llvm def _nan() -> float: ret double 0x7FF8000000000000 e = 2.7182818284590452354 pi = 3.14159265358979323846 tau = 6.28318530717958647693 inf = _inf() nan = _nan() def factorial(x: int) -> int: _F = ( 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000, ) if not (0 <= x <= 20): raise ValueError("factorial is only supported for 0 <= x <= 20") return _F[x] def isnan(x: float) -> bool: """ isnan(float) -> bool Return True if float arg is a NaN, else False. """ @pure @llvm def f(x: float) -> bool: %y = fcmp uno double %x, 0.000000e+00 %z = zext i1 %y to i8 ret i8 %z return f(x) def isinf(x: float) -> bool: """ isinf(float) -> bool: Return True if float arg is an INF, else False. """ @pure @llvm def f(x: float) -> bool: declare double @llvm.fabs.f64(double) %a = call double @llvm.fabs.f64(double %x) %b = fcmp oeq double %a, 0x7FF0000000000000 %c = zext i1 %b to i8 ret i8 %c return f(x) def isfinite(x: float) -> bool: """ isfinite(float) -> bool Return True if x is neither an infinity nor a NaN, and False otherwise. """ return not (isnan(x) or isinf(x)) def _check1(arg: float, r: float, can_overflow: bool = False): if __py_numerics__: if isnan(r) and not isnan(arg): raise ValueError("math domain error") if isinf(r) and isfinite(arg): if can_overflow: raise OverflowError("math range error") else: raise ValueError("math domain error") return r def _check2(x: float, y: float, r: float, can_overflow: bool = False): if __py_numerics__: if isnan(r) and not isnan(x) and not isnan(y): raise ValueError("math domain error") if isinf(r) and isfinite(x) and isfinite(y): if can_overflow: raise OverflowError("math range error") else: raise ValueError("math domain error") return r def ceil(x: float) -> int: """ ceil(float) -> int Return the ceiling of x as an Integral. This is the smallest integer >= x. """ @pure @llvm def f(x: float) -> float: declare double @llvm.ceil.f64(double) %y = call double @llvm.ceil.f64(double %x) ret double %y return int(f(x)) def floor(x: float) -> int: """ floor(float) -> int Return the floor of x as an Integral. This is the largest integer <= x. """ @pure @llvm def f(x: float) -> float: declare double @llvm.floor.f64(double) %y = call double @llvm.floor.f64(double %x) ret double %y return int(f(x)) def fabs(x: float) -> float: """ fabs(float) -> float Returns the absolute value of a floating point number. """ @pure @llvm def f(x: float) -> float: declare double @llvm.fabs.f64(double) %y = call double @llvm.fabs.f64(double %x) ret double %y return f(x) def fmod(x: float, y: float) -> float: """ fmod(float, float) -> float Returns the remainder of x divided by y. """ @pure @llvm def f(x: float, y: float) -> float: %z = frem double %x, %y ret double %z return f(x, y) def exp(x: float) -> float: """ exp(float) -> float Returns the value of e raised to the xth power. """ @pure @llvm def f(x: float) -> float: declare double @llvm.exp.f64(double) %y = call double @llvm.exp.f64(double %x) ret double %y return _check1(x, f(x), True) def expm1(x: float) -> float: """ expm1(float) -> float Return e raised to the power x, minus 1. expm1 provides a way to compute this quantity to full precision. """ return _check1(x, _C.expm1(x), True) def ldexp(x: float, i: int) -> float: """ ldexp(float, int) -> float Returns x multiplied by 2 raised to the power of exponent. """ return _check1(x, _C.ldexp(x, i32(i)), True) def log(x: float, base: float = e) -> float: """ log(float) -> float Returns the natural logarithm (base-e logarithm) of x. """ @pure @llvm def f(x: float) -> float: declare double @llvm.log.f64(double) %y = call double @llvm.log.f64(double %x) ret double %y if base == e: return _check1(x, f(x)) else: return _check1(x, f(x)) / _check1(base, f(base)) def log2(x: float) -> float: """ log2(float) -> float Return the base-2 logarithm of x. """ @pure @llvm def f(x: float) -> float: declare double @llvm.log2.f64(double) %y = call double @llvm.log2.f64(double %x) ret double %y return _check1(x, f(x)) def log10(x: float) -> float: """ log10(float) -> float Returns the common logarithm (base-10 logarithm) of x. """ @pure @llvm def f(x: float) -> float: declare double @llvm.log10.f64(double) %y = call double @llvm.log10.f64(double %x) ret double %y return _check1(x, f(x)) def degrees(x: float) -> float: """ degrees(float) -> float Convert angle x from radians to degrees. """ radToDeg = 180.0 / pi return x * radToDeg def radians(x: float) -> float: """ radians(float) -> float Convert angle x from degrees to radians. """ degToRad = pi / 180.0 return x * degToRad def sqrt(x: float) -> float: """ sqrt(float) -> float Returns the square root of x. """ @pure @llvm def f(x: float) -> float: declare double @llvm.sqrt.f64(double) %y = call double @llvm.sqrt.f64(double %x) ret double %y return _check1(x, f(x)) def pow(x: float, y: float) -> float: """ pow(float, float) -> float Returns x raised to the power of y. """ @pure @llvm def f(x: float, y: float) -> float: declare double @llvm.pow.f64(double, double) %z = call double @llvm.pow.f64(double %x, double %y) ret double %z return _check2(x, y, f(x, y), True) def acos(x: float) -> float: """ acos(float) -> float Returns the arc cosine of x in radians. """ return _check1(x, _C.acos(x)) def asin(x: float) -> float: """ asin(float) -> float Returns the arc sine of x in radians. """ return _check1(x, _C.asin(x)) def atan(x: float) -> float: """ atan(float) -> float Returns the arc tangent of x in radians. """ return _check1(x, _C.atan(x)) def atan2(y: float, x: float) -> float: """ atan2(float, float) -> float Returns the arc tangent in radians of y/x based on the signs of both values to determine the correct quadrant. """ return _check2(x, y, _C.atan2(y, x)) def cos(x: float) -> float: """ cos(float) -> float Returns the cosine of a radian angle x. """ @pure @llvm def f(x: float) -> float: declare double @llvm.cos.f64(double) %y = call double @llvm.cos.f64(double %x) ret double %y return _check1(x, f(x)) def sin(x: float) -> float: """ sin(float) -> float Returns the sine of a radian angle x. """ @pure @llvm def f(x: float) -> float: declare double @llvm.sin.f64(double) %y = call double @llvm.sin.f64(double %x) ret double %y return _check1(x, f(x)) def hypot(x: float, y: float) -> float: """ hypot(float, float) -> float Return the Euclidean norm. This is the length of the vector from the origin to point (x, y). """ return _check2(x, y, _C.hypot(x, y), True) def tan(x: float) -> float: """ tan(float) -> float Return the tangent of a radian angle x. """ return _check1(x, _C.tan(x)) def cosh(x: float) -> float: """ cosh(float) -> float Returns the hyperbolic cosine of x. """ return _check1(x, _C.cosh(x), True) def sinh(x: float) -> float: """ sinh(float) -> float Returns the hyperbolic sine of x. """ return _check1(x, _C.sinh(x), True) def tanh(x: float) -> float: """ tanh(float) -> float Returns the hyperbolic tangent of x. """ return _check1(x, _C.tanh(x)) def acosh(x: float) -> float: """ acosh(float) -> float Return the inverse hyperbolic cosine of x. """ return _check1(x, _C.acosh(x)) def asinh(x: float) -> float: """ asinh(float) -> float Return the inverse hyperbolic sine of x. """ return _check1(x, _C.asinh(x)) def atanh(x: float) -> float: """ atanh(float) -> float Return the inverse hyperbolic tangent of x. """ return _check1(x, _C.atanh(x)) def copysign(x: float, y: float) -> float: """ copysign(float, float) -> float Return a float with the magnitude (absolute value) of x but the sign of y. """ @pure @llvm def f(x: float, y: float) -> float: declare double @llvm.copysign.f64(double, double) %z = call double @llvm.copysign.f64(double %x, double %y) ret double %z return _check2(x, y, f(x, y)) def log1p(x: float) -> float: """ log1p(float) -> float Return the natural logarithm of 1+x (base e). """ return _check1(x, _C.log1p(x)) def trunc(x: float) -> int: """ trunc(float) -> int Return the Real value x truncated to an Integral (usually an integer). """ @pure @llvm def f(x: float) -> float: declare double @llvm.trunc.f64(double) %y = call double @llvm.trunc.f64(double %x) ret double %y return int(_check1(x, f(x))) def erf(x: float) -> float: """ erf(float) -> float Return the error function at x. """ return _check1(x, _C.erf(x)) def erfc(x: float) -> float: """ erfc(float) -> float Return the complementary error function at x. """ return _check1(x, _C.erfc(x)) def gamma(x: float) -> float: """ gamma(float) -> float Return the Gamma function at x. """ return _check1(x, _C.tgamma(x), True) def lgamma(x: float) -> float: """ lgamma(float) -> float Return the natural logarithm of the absolute value of the Gamma function at x. """ return _check1(x, _C.lgamma(x), True) def remainder(x: float, y: float) -> float: """ remainder(float, float) -> float Return the IEEE 754-style remainder of x with respect to y. For finite x and finite nonzero y, this is the difference x - n*y, where n is the closest integer to the exact value of the quotient x / y. If x / y is exactly halfway between two consecutive integers, the nearest even integer is used for n. """ return _check2(x, y, _C.remainder(x, y)) def _gcd2(a, b): a = abs(a) b = abs(b) while a: a, b = b % a, a return b def gcd(*args): """ gcd(*args) returns greatest common divisor of arguments. """ if staticlen(args) == 0: return 0 res = args[0] T = type(res) if staticlen(args) == 1: return abs(res) for i in range(1, len(args)): if res == T(1): break res = _gcd2(res, args[i]) return res def lcm(*args): """ lcm(*args) returns least common multiple of arguments. """ def lcm2(a, b): return abs((a // _gcd2(a, b)) * b) if staticlen(args) == 0: return 1 res = args[0] T = type(res) if staticlen(args) == 1: return abs(res) for i in range(1, len(args)): if not res: break res = lcm2(res, args[i]) return res @pure def frexp(x: float) -> Tuple[float, int]: """ frexp(float) -> Tuple[float, int] The returned value is the mantissa and the integer pointed to by exponent is the exponent. The resultant value is x = mantissa * 2 ^ exponent. """ tmp = i32(0) res = _C.frexp(float(x), __ptr__(tmp)) return (res, int(tmp)) @pure def modf(x: float) -> Tuple[float, float]: """ modf(float) -> Tuple[float, float] The returned value is the fraction component (part after the decimal), and sets integer to the integer component. """ tmp = 0.0 res = _C.modf(float(x), __ptr__(tmp)) return (res, tmp) def isclose(a: float, b: float, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> bool: """ isclose(float, float) -> bool Return True if a is close in value to b, and False otherwise. For the values to be considered close, the difference between them must be smaller than at least one of the tolerances. """ # short circuit exact equality -- needed to catch two # infinities of the same sign. And perhaps speeds things # up a bit sometimes. if a == b: return True # This catches the case of two infinities of opposite sign, or # one infinity and one finite number. Two infinities of opposite # sign would otherwise have an infinite relative tolerance. # Two infinities of the same sign are caught by the equality check # above. if a == inf or b == inf: return False # NAN is not close to anything, not even itself if a == nan or b == nan: return False # regular computation diff = fabs(b - a) return ((diff <= fabs(rel_tol * b)) or (diff <= fabs(rel_tol * a))) or ( diff <= abs_tol ) def fsum(seq): def _fsum_realloc(p: Ptr[float], ps: Ptr[float], n: int, m: int): from internal.gc import realloc, sizeof v = Ptr[float]() m += m if n < m: if p == ps: v = Ptr[float](m) str.memcpy(v.as_byte(), ps.as_byte(), n * sizeof(float)) else: v = Ptr[float](realloc(p.as_byte(), m * sizeof(float), n * sizeof(float))) return v, m _NUM_PARTIALS: Static[int] = 32 ps_arr = __array__[float](_NUM_PARTIALS) ps = ps_arr.ptr p = ps n, m = 0, _NUM_PARTIALS xsave, special_sum, inf_sum = 0.0, 0.0, 0.0 hi, yr, lo = 0.0, 0.0, 0.0 for item in seq: x = float(item) xsave = x i = 0 for j in range(n): # for y in partials y = p[j] if fabs(x) < fabs(y): x, y = y, x hi = x + y yr = hi - x lo = y - yr if lo != 0.0: p[i] = lo i += 1 x = hi n = i if x != 0.0: if not isfinite(x): # a nonfinite x could arise either as # a result of intermediate overflow, or if isfinite(xsave): raise OverflowError("intermediate overflow in fsum") if isinf(xsave): inf_sum += xsave special_sum += xsave # reset partials n = 0 else: if n >= m: p, m = _fsum_realloc(p, ps, n, m) p[n] = x n += 1 if special_sum != 0.0: if isnan(inf_sum): raise ValueError("-inf + inf in fsum") else: return special_sum hi = 0.0 if n > 0: hi = p[n - 1] n -= 1 # sum_exact(ps, hi) from the top, stop when the sum becomes inexact while n > 0: x = hi y = p[n - 1] n -= 1 # assert fabs(y) < fabs(x) hi = x + y yr = hi - x lo = y - yr if lo != 0.0: break # Make half-even rounding work across multiple partials. # Needed so that sum([1e-16, 1, 1e16]) will round-up the last # digit to two instead of down to zero (the 1e-16 makes the 1 # slightly closer to two). With a potential 1 ULP rounding # error fixed-up, math.fsum() can guarantee commutativity. if n > 0 and ((lo < 0.0 and p[n-1] < 0.0) or (lo > 0.0 and p[n-1] > 0.0)): y = lo * 2.0 x = hi + y yr = x - hi if y == yr: hi = x return hi def prod(iterable, start = 1): def prod_generator(iterable: Generator[T], start, T: type): if T is float: result = float(start) else: result = start for a in iterable: result *= a return result def prod_tuple(iterable, start): if staticlen(iterable) == 0: return start else: return prod(iterable[1:], start=(start * iterable[0])) if isinstance(iterable, Tuple): return prod_tuple(iterable, start) else: return prod_generator(iterable, start) # 32-bit float ops e32 = float32(e) pi32 = float32(pi) tau32 = float32(tau) inf32 = float32(inf) nan32 = float32(nan) @overload def isnan(x: float32) -> bool: """ isnan(float32) -> bool Return True if float arg is a NaN, else False. """ @pure @llvm def f(x: float32) -> bool: %y = fcmp uno float %x, 0.000000e+00 %z = zext i1 %y to i8 ret i8 %z return f(x) @overload def isinf(x: float32) -> bool: """ isinf(float32) -> bool: Return True if float arg is an INF, else False. """ @pure @llvm def f(x: float32) -> bool: declare float @llvm.fabs.f32(float) %a = call float @llvm.fabs.f32(float %x) %b = fcmp oeq float %a, 0x7FF0000000000000 %c = zext i1 %b to i8 ret i8 %c return f(x) @overload def isfinite(x: float32) -> bool: """ isfinite(float32) -> bool Return True if x is neither an infinity nor a NaN, and False otherwise. """ return not (isnan(x) or isinf(x)) @overload def ceil(x: float32) -> int: """ ceil(float32) -> int Return the ceiling of x as an Integral. This is the smallest integer >= x. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.ceil.f32(float) %y = call float @llvm.ceil.f32(float %x) ret float %y return int(f(x)) @overload def floor(x: float32) -> int: """ floor(float32) -> int Return the floor of x as an Integral. This is the largest integer <= x. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.floor.f32(float) %y = call float @llvm.floor.f32(float %x) ret float %y return int(f(x)) @overload def fabs(x: float32) -> float32: """ fabs(float32) -> float32 Returns the absolute value of a float32ing point number. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.fabs.f32(float) %y = call float @llvm.fabs.f32(float %x) ret float %y return f(x) @overload def fmod(x: float32, y: float32) -> float32: """ fmod(float32, float32) -> float32 Returns the remainder of x divided by y. """ @pure @llvm def f(x: float32, y: float32) -> float32: %z = frem float %x, %y ret float %z return f(x, y) @overload def exp(x: float32) -> float32: """ exp(float32) -> float32 Returns the value of e raised to the xth power. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.exp.f32(float) %y = call float @llvm.exp.f32(float %x) ret float %y return f(x) @overload def expm1(x: float32) -> float32: """ expm1(float32) -> float32 Return e raised to the power x, minus 1. expm1 provides a way to compute this quantity to full precision. """ return _C.expm1f(x) @overload def ldexp(x: float32, i: int) -> float32: """ ldexp(float32, int) -> float32 Returns x multiplied by 2 raised to the power of exponent. """ return _C.ldexpf(x, i32(i)) @overload def log(x: float32, base: float32 = e32) -> float32: """ log(float32) -> float32 Returns the natural logarithm (base-e logarithm) of x. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.log.f32(float) %y = call float @llvm.log.f32(float %x) ret float %y if base == e32: return f(x) else: return f(x) / f(base) @overload def log2(x: float32) -> float32: """ log2(float32) -> float32 Return the base-2 logarithm of x. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.log2.f32(float) %y = call float @llvm.log2.f32(float %x) ret float %y return f(x) @overload def log10(x: float32) -> float32: """ log10(float32) -> float32 Returns the common logarithm (base-10 logarithm) of x. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.log10.f32(float) %y = call float @llvm.log10.f32(float %x) ret float %y return f(x) @overload def degrees(x: float32) -> float32: """ degrees(float32) -> float32 Convert angle x from radians to degrees. """ radToDeg = float32(180.0) / pi32 return x * radToDeg @overload def radians(x: float32) -> float32: """ radians(float32) -> float32 Convert angle x from degrees to radians. """ degToRad = pi32 / float32(180.0) return x * degToRad @overload def sqrt(x: float32) -> float32: """ sqrt(float32) -> float32 Returns the square root of x. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.sqrt.f32(float) %y = call float @llvm.sqrt.f32(float %x) ret float %y return f(x) @overload def pow(x: float32, y: float32) -> float32: """ pow(float32, float32) -> float32 Returns x raised to the power of y. """ @pure @llvm def f(x: float32, y: float32) -> float32: declare float @llvm.pow.f32(float, float) %z = call float @llvm.pow.f32(float %x, float %y) ret float %z return f(x, y) @overload def acos(x: float32) -> float32: """ acos(float32) -> float32 Returns the arc cosine of x in radians. """ return _C.acosf(x) @overload def asin(x: float32) -> float32: """ asin(float32) -> float32 Returns the arc sine of x in radians. """ return _C.asinf(x) @overload def atan(x: float32) -> float32: """ atan(float32) -> float32 Returns the arc tangent of x in radians. """ return _C.atanf(x) @overload def atan2(y: float32, x: float32) -> float32: """ atan2(float32, float32) -> float32 Returns the arc tangent in radians of y/x based on the signs of both values to determine the correct quadrant. """ return _C.atan2f(y, x) @overload def cos(x: float32) -> float32: """ cos(float32) -> float32 Returns the cosine of a radian angle x. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.cos.f32(float) %y = call float @llvm.cos.f32(float %x) ret float %y return f(x) @overload def sin(x: float32) -> float32: """ sin(float32) -> float32 Returns the sine of a radian angle x. """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.sin.f32(float) %y = call float @llvm.sin.f32(float %x) ret float %y return f(x) @overload def hypot(x: float32, y: float32) -> float32: """ hypot(float32, float32) -> float32 Return the Euclidean norm. This is the length of the vector from the origin to point (x, y). """ return _C.hypotf(x, y) @overload def tan(x: float32) -> float32: """ tan(float32) -> float32 Return the tangent of a radian angle x. """ return _C.tanf(x) @overload def cosh(x: float32) -> float32: """ cosh(float32) -> float32 Returns the hyperbolic cosine of x. """ return _C.coshf(x) @overload def sinh(x: float32) -> float32: """ sinh(float32) -> float32 Returns the hyperbolic sine of x. """ return _C.sinhf(x) @overload def tanh(x: float32) -> float32: """ tanh(float32) -> float32 Returns the hyperbolic tangent of x. """ return _C.tanhf(x) @overload def acosh(x: float32) -> float32: """ acosh(float32) -> float32 Return the inverse hyperbolic cosine of x. """ return _C.acoshf(x) @overload def asinh(x: float32) -> float32: """ asinh(float32) -> float32 Return the inverse hyperbolic sine of x. """ return _C.asinhf(x) @overload def atanh(x: float32) -> float32: """ atanh(float32) -> float32 Return the inverse hyperbolic tangent of x. """ return _C.atanhf(x) @overload def copysign(x: float32, y: float32) -> float32: """ copysign(float32, float32) -> float32 Return a float32 with the magnitude (absolute value) of x but the sign of y. """ @pure @llvm def f(x: float32, y: float32) -> float32: declare float @llvm.copysign.f32(float, float) %z = call float @llvm.copysign.f32(float %x, float %y) ret float %z return f(x, y) @overload def log1p(x: float32) -> float32: """ log1p(float32) -> float32 Return the natural logarithm of 1+x (base e). """ return _C.log1pf(x) @overload def trunc(x: float32) -> int: """ trunc(float32) -> int Return the Real value x truncated to an Integral (usually an integer). """ @pure @llvm def f(x: float32) -> float32: declare float @llvm.trunc.f32(float) %y = call float @llvm.trunc.f32(float %x) ret float %y return int(f(x)) @overload def erf(x: float32) -> float32: """ erf(float32) -> float32 Return the error function at x. """ return _C.erff(x) @overload def erfc(x: float32) -> float32: """ erfc(float32) -> float32 Return the complementary error function at x. """ return _C.erfcf(x) @overload def gamma(x: float32) -> float32: """ gamma(float32) -> float32 Return the Gamma function at x. """ return _C.tgammaf(x) @overload def lgamma(x: float32) -> float32: """ lgamma(float32) -> float32 Return the natural logarithm of the absolute value of the Gamma function at x. """ return _C.lgammaf(x) @overload def remainder(x: float32, y: float32) -> float32: """ remainder(float32, float32) -> float32 Return the IEEE 754-style remainder of x with respect to y. For finite x and finite nonzero y, this is the difference x - n*y, where n is the closest integer to the exact value of the quotient x / y. If x / y is exactly halfway between two consecutive integers, the nearest even integer is used for n. """ return _C.remainderf(x, y) @overload @pure def frexp(x: float32) -> Tuple[float32, int]: """ frexp(float32) -> Tuple[float32, int] The returned value is the mantissa and the integer pointed to by exponent is the exponent. The resultant value is x = mantissa * 2 ^ exponent. """ tmp = i32(0) res = _C.frexpf(float32(x), __ptr__(tmp)) return (res, int(tmp)) @overload @pure def modf(x: float32) -> Tuple[float32, float32]: """ modf(float32) -> Tuple[float32, float32] The returned value is the fraction component (part after the decimal), and sets integer to the integer component. """ tmp = float32(0.0) res = _C.modff(float32(x), __ptr__(tmp)) return (res, tmp) @overload def isclose(a: float32, b: float32, rel_tol: float32 = float32(1e-09), abs_tol: float32 = float32(0.0)) -> bool: """ isclose(float32, float32) -> bool Return True if a is close in value to b, and False otherwise. For the values to be considered close, the difference between them must be smaller than at least one of the tolerances. """ # short circuit exact equality -- needed to catch two # infinities of the same sign. And perhaps speeds things # up a bit sometimes. if a == b: return True # This catches the case of two infinities of opposite sign, or # one infinity and one finite number. Two infinities of opposite # sign would otherwise have an infinite relative tolerance. # Two infinities of the same sign are caught by the equality check # above. if a == inf32 or b == inf32: return False # NAN is not close to anything, not even itself if a == nan32 or b == nan32: return False # regular computation diff = fabs(b - a) return ((diff <= fabs(rel_tol * b)) or (diff <= fabs(rel_tol * a))) or ( diff <= abs_tol )