PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

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

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