PageRenderTime 215ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

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

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