PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/rpython/lltypesystem/module/ll_math.py

https://bitbucket.org/pypy/pypy/
Python | 430 lines | 321 code | 46 blank | 63 comment | 97 complexity | a2823afb42ed13dd46004d3925d574c3 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import math
  2. import errno
  3. import py
  4. import sys
  5. from pypy.rpython.lltypesystem import lltype, rffi
  6. from pypy.tool.sourcetools import func_with_new_name
  7. from pypy.tool.autopath import pypydir
  8. from pypy.rlib import jit, rposix
  9. from pypy.translator.tool.cbuild import ExternalCompilationInfo
  10. from pypy.translator.platform import platform
  11. from pypy.rlib.rfloat import isfinite, isinf, isnan, INFINITY, NAN
  12. use_library_isinf_isnan = False
  13. if sys.platform == "win32":
  14. if platform.name == "msvc":
  15. # When compiled with /O2 or /Oi (enable intrinsic functions)
  16. # It's no more possible to take the address of some math functions.
  17. # Ensure that the compiler chooses real functions instead.
  18. eci = ExternalCompilationInfo(
  19. includes = ['math.h', 'float.h'],
  20. post_include_bits = ['#pragma function(floor)'],
  21. )
  22. use_library_isinf_isnan = True
  23. else:
  24. eci = ExternalCompilationInfo()
  25. # Some math functions are C99 and not defined by the Microsoft compiler
  26. cdir = py.path.local(pypydir).join('translator', 'c')
  27. math_eci = ExternalCompilationInfo(
  28. include_dirs = [cdir],
  29. includes = ['src/ll_math.h'],
  30. separate_module_files=[cdir.join('src', 'll_math.c')],
  31. export_symbols=['_pypy_math_acosh', '_pypy_math_asinh',
  32. '_pypy_math_atanh',
  33. '_pypy_math_expm1', '_pypy_math_log1p'],
  34. )
  35. math_prefix = '_pypy_math_'
  36. else:
  37. eci = ExternalCompilationInfo(
  38. libraries=['m'])
  39. math_eci = eci
  40. math_prefix = ''
  41. def llexternal(name, ARGS, RESULT, **kwargs):
  42. return rffi.llexternal(name, ARGS, RESULT, compilation_info=eci,
  43. sandboxsafe=True, **kwargs)
  44. def math_llexternal(name, ARGS, RESULT):
  45. return rffi.llexternal(math_prefix + name, ARGS, RESULT,
  46. compilation_info=math_eci,
  47. sandboxsafe=True)
  48. if sys.platform == 'win32':
  49. underscore = '_'
  50. else:
  51. underscore = ''
  52. math_fabs = llexternal('fabs', [rffi.DOUBLE], rffi.DOUBLE)
  53. math_log = llexternal('log', [rffi.DOUBLE], rffi.DOUBLE)
  54. math_log10 = llexternal('log10', [rffi.DOUBLE], rffi.DOUBLE)
  55. math_copysign = llexternal(underscore + 'copysign',
  56. [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE,
  57. elidable_function=True)
  58. math_atan2 = llexternal('atan2', [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE)
  59. math_frexp = llexternal('frexp', [rffi.DOUBLE, rffi.INTP], rffi.DOUBLE)
  60. math_modf = llexternal('modf', [rffi.DOUBLE, rffi.DOUBLEP], rffi.DOUBLE)
  61. math_ldexp = llexternal('ldexp', [rffi.DOUBLE, rffi.INT], rffi.DOUBLE)
  62. math_pow = llexternal('pow', [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE)
  63. math_fmod = llexternal('fmod', [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE)
  64. math_hypot = llexternal(underscore + 'hypot',
  65. [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE)
  66. math_floor = llexternal('floor', [rffi.DOUBLE], rffi.DOUBLE, elidable_function=True)
  67. math_sqrt = llexternal('sqrt', [rffi.DOUBLE], rffi.DOUBLE)
  68. math_sin = llexternal('sin', [rffi.DOUBLE], rffi.DOUBLE)
  69. math_cos = llexternal('cos', [rffi.DOUBLE], rffi.DOUBLE)
  70. @jit.elidable
  71. def sqrt_nonneg(x):
  72. return math_sqrt(x)
  73. sqrt_nonneg.oopspec = "math.sqrt_nonneg(x)"
  74. # ____________________________________________________________
  75. #
  76. # Error handling functions
  77. ERANGE = errno.ERANGE
  78. EDOM = errno.EDOM
  79. def _error_reset():
  80. rposix.set_errno(0)
  81. def _likely_raise(errno, x):
  82. """Call this with errno != 0. It usually raises the proper RPython
  83. exception, but may also just ignore it and return in case of underflow.
  84. """
  85. assert errno
  86. if errno == ERANGE:
  87. # We consider underflow to not be an error, like CPython.
  88. # On some platforms (Ubuntu/ia64) it seems that errno can be
  89. # set to ERANGE for subnormal results that do *not* underflow
  90. # to zero. So to be safe, we'll ignore ERANGE whenever the
  91. # function result is less than one in absolute value.
  92. if math_fabs(x) < 1.0:
  93. return
  94. raise OverflowError("math range error")
  95. else:
  96. raise ValueError("math domain error")
  97. # ____________________________________________________________
  98. #
  99. # Custom implementations
  100. VERY_LARGE_FLOAT = 1.0
  101. while VERY_LARGE_FLOAT * 100.0 != INFINITY:
  102. VERY_LARGE_FLOAT *= 64.0
  103. _lib_isnan = rffi.llexternal("_isnan", [lltype.Float], lltype.Signed,
  104. compilation_info=eci)
  105. _lib_finite = rffi.llexternal("_finite", [lltype.Float], lltype.Signed,
  106. compilation_info=eci)
  107. def ll_math_isnan(y):
  108. # By not calling into the external function the JIT can inline this.
  109. # Floats are awesome.
  110. if use_library_isinf_isnan and not jit.we_are_jitted():
  111. return bool(_lib_isnan(y))
  112. return y != y
  113. def ll_math_isinf(y):
  114. if jit.we_are_jitted():
  115. return (y + VERY_LARGE_FLOAT) == y
  116. elif use_library_isinf_isnan:
  117. return not _lib_finite(y) and not _lib_isnan(y)
  118. else:
  119. return y == INFINITY or y == -INFINITY
  120. def ll_math_isfinite(y):
  121. # Use a custom hack that is reasonably well-suited to the JIT.
  122. # Floats are awesome (bis).
  123. if use_library_isinf_isnan and not jit.we_are_jitted():
  124. return bool(_lib_finite(y))
  125. z = 0.0 * y
  126. return z == z # i.e.: z is not a NaN
  127. ll_math_floor = math_floor
  128. ll_math_copysign = math_copysign
  129. def ll_math_atan2(y, x):
  130. """wrapper for atan2 that deals directly with special cases before
  131. delegating to the platform libm for the remaining cases. This
  132. is necessary to get consistent behaviour across platforms.
  133. Windows, FreeBSD and alpha Tru64 are amongst platforms that don't
  134. always follow C99.
  135. """
  136. if isnan(x):
  137. return NAN
  138. if not isfinite(y):
  139. if isnan(y):
  140. return NAN
  141. if isinf(x):
  142. if math_copysign(1.0, x) == 1.0:
  143. # atan2(+-inf, +inf) == +-pi/4
  144. return math_copysign(0.25 * math.pi, y)
  145. else:
  146. # atan2(+-inf, -inf) == +-pi*3/4
  147. return math_copysign(0.75 * math.pi, y)
  148. # atan2(+-inf, x) == +-pi/2 for finite x
  149. return math_copysign(0.5 * math.pi, y)
  150. if isinf(x) or y == 0.0:
  151. if math_copysign(1.0, x) == 1.0:
  152. # atan2(+-y, +inf) = atan2(+-0, +x) = +-0.
  153. return math_copysign(0.0, y)
  154. else:
  155. # atan2(+-y, -inf) = atan2(+-0., -x) = +-pi.
  156. return math_copysign(math.pi, y)
  157. return math_atan2(y, x)
  158. # XXX Various platforms (Solaris, OpenBSD) do nonstandard things for log(0),
  159. # log(-ve), log(NaN). For now I'm ignoring this issue as these are a bit
  160. # more marginal platforms for us.
  161. def ll_math_frexp(x):
  162. # deal with special cases directly, to sidestep platform differences
  163. if not isfinite(x) or not x:
  164. mantissa = x
  165. exponent = 0
  166. else:
  167. exp_p = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
  168. try:
  169. mantissa = math_frexp(x, exp_p)
  170. exponent = rffi.cast(lltype.Signed, exp_p[0])
  171. finally:
  172. lltype.free(exp_p, flavor='raw')
  173. return (mantissa, exponent)
  174. INT_MAX = int(2**31-1)
  175. INT_MIN = int(-2**31)
  176. def ll_math_ldexp(x, exp):
  177. if x == 0.0 or not isfinite(x):
  178. return x # NaNs, zeros and infinities are returned unchanged
  179. if exp > INT_MAX:
  180. # overflow (64-bit platforms only)
  181. r = math_copysign(INFINITY, x)
  182. errno = ERANGE
  183. elif exp < INT_MIN:
  184. # underflow to +-0 (64-bit platforms only)
  185. r = math_copysign(0.0, x)
  186. errno = 0
  187. else:
  188. _error_reset()
  189. r = math_ldexp(x, exp)
  190. errno = rposix.get_errno()
  191. if isinf(r):
  192. errno = ERANGE
  193. if errno:
  194. _likely_raise(errno, r)
  195. return r
  196. def ll_math_modf(x):
  197. # some platforms don't do the right thing for NaNs and
  198. # infinities, so we take care of special cases directly.
  199. if not isfinite(x):
  200. if isnan(x):
  201. return (x, x)
  202. else: # isinf(x)
  203. return (math_copysign(0.0, x), x)
  204. intpart_p = lltype.malloc(rffi.DOUBLEP.TO, 1, flavor='raw')
  205. try:
  206. fracpart = math_modf(x, intpart_p)
  207. intpart = intpart_p[0]
  208. finally:
  209. lltype.free(intpart_p, flavor='raw')
  210. return (fracpart, intpart)
  211. def ll_math_fmod(x, y):
  212. # fmod(x, +/-Inf) returns x for finite x.
  213. if isinf(y) and isfinite(x):
  214. return x
  215. _error_reset()
  216. r = math_fmod(x, y)
  217. errno = rposix.get_errno()
  218. if isnan(r):
  219. if isnan(x) or isnan(y):
  220. errno = 0
  221. else:
  222. errno = EDOM
  223. if errno:
  224. _likely_raise(errno, r)
  225. return r
  226. def ll_math_hypot(x, y):
  227. # hypot(x, +/-Inf) returns Inf, even if x is a NaN.
  228. if isinf(x):
  229. return math_fabs(x)
  230. if isinf(y):
  231. return math_fabs(y)
  232. _error_reset()
  233. r = math_hypot(x, y)
  234. errno = rposix.get_errno()
  235. if not isfinite(r):
  236. if isnan(r):
  237. if isnan(x) or isnan(y):
  238. errno = 0
  239. else:
  240. errno = EDOM
  241. else: # isinf(r)
  242. if isfinite(x) and isfinite(y):
  243. errno = ERANGE
  244. else:
  245. errno = 0
  246. if errno:
  247. _likely_raise(errno, r)
  248. return r
  249. def ll_math_pow(x, y):
  250. # deal directly with IEEE specials, to cope with problems on various
  251. # platforms whose semantics don't exactly match C99
  252. if isnan(y):
  253. if x == 1.0:
  254. return 1.0 # 1**Nan = 1
  255. return y
  256. if not isfinite(x):
  257. if isnan(x):
  258. if y == 0.0:
  259. return 1.0 # NaN**0 = 1
  260. return x
  261. else: # isinf(x)
  262. odd_y = not isinf(y) and math_fmod(math_fabs(y), 2.0) == 1.0
  263. if y > 0.0:
  264. if odd_y:
  265. return x
  266. return math_fabs(x)
  267. elif y == 0.0:
  268. return 1.0
  269. else: # y < 0.0
  270. if odd_y:
  271. return math_copysign(0.0, x)
  272. return 0.0
  273. if isinf(y):
  274. if math_fabs(x) == 1.0:
  275. return 1.0
  276. elif y > 0.0 and math_fabs(x) > 1.0:
  277. return y
  278. elif y < 0.0 and math_fabs(x) < 1.0:
  279. if x == 0.0:
  280. raise ValueError("0**-inf: divide by zero")
  281. return -y # result is +inf
  282. else:
  283. return 0.0
  284. _error_reset()
  285. r = math_pow(x, y)
  286. errno = rposix.get_errno()
  287. if not isfinite(r):
  288. if isnan(r):
  289. # a NaN result should arise only from (-ve)**(finite non-integer)
  290. errno = EDOM
  291. else: # isinf(r)
  292. # an infinite result here arises either from:
  293. # (A) (+/-0.)**negative (-> divide-by-zero)
  294. # (B) overflow of x**y with x and y finite
  295. if x == 0.0:
  296. errno = EDOM
  297. else:
  298. errno = ERANGE
  299. if errno:
  300. _likely_raise(errno, r)
  301. return r
  302. def ll_math_sqrt(x):
  303. if x < 0.0:
  304. raise ValueError, "math domain error"
  305. if isfinite(x):
  306. return sqrt_nonneg(x)
  307. return x # +inf or nan
  308. def ll_math_log(x):
  309. if x <= 0:
  310. raise ValueError("math domain error")
  311. return math_log(x)
  312. def ll_math_log10(x):
  313. if x <= 0:
  314. raise ValueError("math domain error")
  315. return math_log10(x)
  316. def ll_math_sin(x):
  317. if isinf(x):
  318. raise ValueError("math domain error")
  319. return math_sin(x)
  320. def ll_math_cos(x):
  321. if isinf(x):
  322. raise ValueError("math domain error")
  323. return math_cos(x)
  324. # ____________________________________________________________
  325. #
  326. # Default implementations
  327. def new_unary_math_function(name, can_overflow, c99):
  328. if sys.platform == 'win32' and c99:
  329. c_func = math_llexternal(name, [rffi.DOUBLE], rffi.DOUBLE)
  330. else:
  331. c_func = llexternal(name, [rffi.DOUBLE], rffi.DOUBLE)
  332. def ll_math(x):
  333. _error_reset()
  334. r = c_func(x)
  335. # Error checking fun. Copied from CPython 2.6
  336. errno = rposix.get_errno()
  337. if not isfinite(r):
  338. if isnan(r):
  339. if isnan(x):
  340. errno = 0
  341. else:
  342. errno = EDOM
  343. else: # isinf(r)
  344. if not isfinite(x):
  345. errno = 0
  346. elif can_overflow:
  347. errno = ERANGE
  348. else:
  349. errno = EDOM
  350. if errno:
  351. _likely_raise(errno, r)
  352. return r
  353. return func_with_new_name(ll_math, 'll_math_' + name)
  354. # ____________________________________________________________
  355. unary_math_functions = [
  356. 'acos', 'asin', 'atan',
  357. 'ceil', 'cosh', 'exp', 'fabs',
  358. 'sinh', 'tan', 'tanh',
  359. 'acosh', 'asinh', 'atanh', 'log1p', 'expm1',
  360. ]
  361. unary_math_functions_can_overflow = [
  362. 'cosh', 'exp', 'log1p', 'sinh', 'expm1',
  363. ]
  364. unary_math_functions_c99 = [
  365. 'acosh', 'asinh', 'atanh', 'log1p', 'expm1',
  366. ]
  367. for name in unary_math_functions:
  368. can_overflow = name in unary_math_functions_can_overflow
  369. c99 = name in unary_math_functions_c99
  370. globals()['ll_math_' + name] = new_unary_math_function(name, can_overflow, c99)