float_ops.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Float primitive operations
  2. //
  3. // These are registered in mypyc.primitives.float_ops.
  4. #include <Python.h>
  5. #include "CPy.h"
  6. static double CPy_DomainError(void) {
  7. PyErr_SetString(PyExc_ValueError, "math domain error");
  8. return CPY_FLOAT_ERROR;
  9. }
  10. static double CPy_MathRangeError(void) {
  11. PyErr_SetString(PyExc_OverflowError, "math range error");
  12. return CPY_FLOAT_ERROR;
  13. }
  14. double CPyFloat_FromTagged(CPyTagged x) {
  15. if (CPyTagged_CheckShort(x)) {
  16. return CPyTagged_ShortAsSsize_t(x);
  17. }
  18. double result = PyFloat_AsDouble(CPyTagged_LongAsObject(x));
  19. if (unlikely(result == -1.0) && PyErr_Occurred()) {
  20. return CPY_FLOAT_ERROR;
  21. }
  22. return result;
  23. }
  24. double CPyFloat_Sin(double x) {
  25. double v = sin(x);
  26. if (unlikely(isnan(v)) && !isnan(x)) {
  27. return CPy_DomainError();
  28. }
  29. return v;
  30. }
  31. double CPyFloat_Cos(double x) {
  32. double v = cos(x);
  33. if (unlikely(isnan(v)) && !isnan(x)) {
  34. return CPy_DomainError();
  35. }
  36. return v;
  37. }
  38. double CPyFloat_Tan(double x) {
  39. if (unlikely(isinf(x))) {
  40. return CPy_DomainError();
  41. }
  42. return tan(x);
  43. }
  44. double CPyFloat_Sqrt(double x) {
  45. if (x < 0.0) {
  46. return CPy_DomainError();
  47. }
  48. return sqrt(x);
  49. }
  50. double CPyFloat_Exp(double x) {
  51. double v = exp(x);
  52. if (unlikely(v == INFINITY) && x != INFINITY) {
  53. return CPy_MathRangeError();
  54. }
  55. return v;
  56. }
  57. double CPyFloat_Log(double x) {
  58. if (x <= 0.0) {
  59. return CPy_DomainError();
  60. }
  61. return log(x);
  62. }
  63. CPyTagged CPyFloat_Floor(double x) {
  64. double v = floor(x);
  65. return CPyTagged_FromFloat(v);
  66. }
  67. CPyTagged CPyFloat_Ceil(double x) {
  68. double v = ceil(x);
  69. return CPyTagged_FromFloat(v);
  70. }
  71. bool CPyFloat_IsInf(double x) {
  72. return isinf(x) != 0;
  73. }
  74. bool CPyFloat_IsNaN(double x) {
  75. return isnan(x) != 0;
  76. }
  77. // From CPython 3.10.0, Objects/floatobject.c
  78. static void
  79. _float_div_mod(double vx, double wx, double *floordiv, double *mod)
  80. {
  81. double div;
  82. *mod = fmod(vx, wx);
  83. /* fmod is typically exact, so vx-mod is *mathematically* an
  84. exact multiple of wx. But this is fp arithmetic, and fp
  85. vx - mod is an approximation; the result is that div may
  86. not be an exact integral value after the division, although
  87. it will always be very close to one.
  88. */
  89. div = (vx - *mod) / wx;
  90. if (*mod) {
  91. /* ensure the remainder has the same sign as the denominator */
  92. if ((wx < 0) != (*mod < 0)) {
  93. *mod += wx;
  94. div -= 1.0;
  95. }
  96. }
  97. else {
  98. /* the remainder is zero, and in the presence of signed zeroes
  99. fmod returns different results across platforms; ensure
  100. it has the same sign as the denominator. */
  101. *mod = copysign(0.0, wx);
  102. }
  103. /* snap quotient to nearest integral value */
  104. if (div) {
  105. *floordiv = floor(div);
  106. if (div - *floordiv > 0.5) {
  107. *floordiv += 1.0;
  108. }
  109. }
  110. else {
  111. /* div is zero - get the same sign as the true quotient */
  112. *floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */
  113. }
  114. }
  115. double CPyFloat_FloorDivide(double x, double y) {
  116. double mod, floordiv;
  117. if (y == 0) {
  118. PyErr_SetString(PyExc_ZeroDivisionError, "float floor division by zero");
  119. return CPY_FLOAT_ERROR;
  120. }
  121. _float_div_mod(x, y, &floordiv, &mod);
  122. return floordiv;
  123. }
  124. // Adapted from CPython 3.10.7
  125. double CPyFloat_Pow(double x, double y) {
  126. if (!isfinite(x) || !isfinite(y)) {
  127. if (isnan(x))
  128. return y == 0.0 ? 1.0 : x; /* NaN**0 = 1 */
  129. else if (isnan(y))
  130. return x == 1.0 ? 1.0 : y; /* 1**NaN = 1 */
  131. else if (isinf(x)) {
  132. int odd_y = isfinite(y) && fmod(fabs(y), 2.0) == 1.0;
  133. if (y > 0.0)
  134. return odd_y ? x : fabs(x);
  135. else if (y == 0.0)
  136. return 1.0;
  137. else /* y < 0. */
  138. return odd_y ? copysign(0.0, x) : 0.0;
  139. }
  140. else if (isinf(y)) {
  141. if (fabs(x) == 1.0)
  142. return 1.0;
  143. else if (y > 0.0 && fabs(x) > 1.0)
  144. return y;
  145. else if (y < 0.0 && fabs(x) < 1.0) {
  146. #if PY_VERSION_HEX < 0x030B0000
  147. if (x == 0.0) { /* 0**-inf: divide-by-zero */
  148. return CPy_DomainError();
  149. }
  150. #endif
  151. return -y; /* result is +inf */
  152. } else
  153. return 0.0;
  154. }
  155. }
  156. double r = pow(x, y);
  157. if (!isfinite(r)) {
  158. if (isnan(r)) {
  159. return CPy_DomainError();
  160. }
  161. /*
  162. an infinite result here arises either from:
  163. (A) (+/-0.)**negative (-> divide-by-zero)
  164. (B) overflow of x**y with x and y finite
  165. */
  166. else if (isinf(r)) {
  167. if (x == 0.0)
  168. return CPy_DomainError();
  169. else
  170. return CPy_MathRangeError();
  171. }
  172. }
  173. return r;
  174. }