| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // Float primitive operations
- //
- // These are registered in mypyc.primitives.float_ops.
- #include <Python.h>
- #include "CPy.h"
- static double CPy_DomainError(void) {
- PyErr_SetString(PyExc_ValueError, "math domain error");
- return CPY_FLOAT_ERROR;
- }
- static double CPy_MathRangeError(void) {
- PyErr_SetString(PyExc_OverflowError, "math range error");
- return CPY_FLOAT_ERROR;
- }
- double CPyFloat_FromTagged(CPyTagged x) {
- if (CPyTagged_CheckShort(x)) {
- return CPyTagged_ShortAsSsize_t(x);
- }
- double result = PyFloat_AsDouble(CPyTagged_LongAsObject(x));
- if (unlikely(result == -1.0) && PyErr_Occurred()) {
- return CPY_FLOAT_ERROR;
- }
- return result;
- }
- double CPyFloat_Sin(double x) {
- double v = sin(x);
- if (unlikely(isnan(v)) && !isnan(x)) {
- return CPy_DomainError();
- }
- return v;
- }
- double CPyFloat_Cos(double x) {
- double v = cos(x);
- if (unlikely(isnan(v)) && !isnan(x)) {
- return CPy_DomainError();
- }
- return v;
- }
- double CPyFloat_Tan(double x) {
- if (unlikely(isinf(x))) {
- return CPy_DomainError();
- }
- return tan(x);
- }
- double CPyFloat_Sqrt(double x) {
- if (x < 0.0) {
- return CPy_DomainError();
- }
- return sqrt(x);
- }
- double CPyFloat_Exp(double x) {
- double v = exp(x);
- if (unlikely(v == INFINITY) && x != INFINITY) {
- return CPy_MathRangeError();
- }
- return v;
- }
- double CPyFloat_Log(double x) {
- if (x <= 0.0) {
- return CPy_DomainError();
- }
- return log(x);
- }
- CPyTagged CPyFloat_Floor(double x) {
- double v = floor(x);
- return CPyTagged_FromFloat(v);
- }
- CPyTagged CPyFloat_Ceil(double x) {
- double v = ceil(x);
- return CPyTagged_FromFloat(v);
- }
- bool CPyFloat_IsInf(double x) {
- return isinf(x) != 0;
- }
- bool CPyFloat_IsNaN(double x) {
- return isnan(x) != 0;
- }
- // From CPython 3.10.0, Objects/floatobject.c
- static void
- _float_div_mod(double vx, double wx, double *floordiv, double *mod)
- {
- double div;
- *mod = fmod(vx, wx);
- /* fmod is typically exact, so vx-mod is *mathematically* an
- exact multiple of wx. But this is fp arithmetic, and fp
- vx - mod is an approximation; the result is that div may
- not be an exact integral value after the division, although
- it will always be very close to one.
- */
- div = (vx - *mod) / wx;
- if (*mod) {
- /* ensure the remainder has the same sign as the denominator */
- if ((wx < 0) != (*mod < 0)) {
- *mod += wx;
- div -= 1.0;
- }
- }
- else {
- /* the remainder is zero, and in the presence of signed zeroes
- fmod returns different results across platforms; ensure
- it has the same sign as the denominator. */
- *mod = copysign(0.0, wx);
- }
- /* snap quotient to nearest integral value */
- if (div) {
- *floordiv = floor(div);
- if (div - *floordiv > 0.5) {
- *floordiv += 1.0;
- }
- }
- else {
- /* div is zero - get the same sign as the true quotient */
- *floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */
- }
- }
- double CPyFloat_FloorDivide(double x, double y) {
- double mod, floordiv;
- if (y == 0) {
- PyErr_SetString(PyExc_ZeroDivisionError, "float floor division by zero");
- return CPY_FLOAT_ERROR;
- }
- _float_div_mod(x, y, &floordiv, &mod);
- return floordiv;
- }
- // Adapted from CPython 3.10.7
- double CPyFloat_Pow(double x, double y) {
- if (!isfinite(x) || !isfinite(y)) {
- if (isnan(x))
- return y == 0.0 ? 1.0 : x; /* NaN**0 = 1 */
- else if (isnan(y))
- return x == 1.0 ? 1.0 : y; /* 1**NaN = 1 */
- else if (isinf(x)) {
- int odd_y = isfinite(y) && fmod(fabs(y), 2.0) == 1.0;
- if (y > 0.0)
- return odd_y ? x : fabs(x);
- else if (y == 0.0)
- return 1.0;
- else /* y < 0. */
- return odd_y ? copysign(0.0, x) : 0.0;
- }
- else if (isinf(y)) {
- if (fabs(x) == 1.0)
- return 1.0;
- else if (y > 0.0 && fabs(x) > 1.0)
- return y;
- else if (y < 0.0 && fabs(x) < 1.0) {
- #if PY_VERSION_HEX < 0x030B0000
- if (x == 0.0) { /* 0**-inf: divide-by-zero */
- return CPy_DomainError();
- }
- #endif
- return -y; /* result is +inf */
- } else
- return 0.0;
- }
- }
- double r = pow(x, y);
- if (!isfinite(r)) {
- if (isnan(r)) {
- return CPy_DomainError();
- }
- /*
- an infinite result here arises either from:
- (A) (+/-0.)**negative (-> divide-by-zero)
- (B) overflow of x**y with x and y finite
- */
- else if (isinf(r)) {
- if (x == 0.0)
- return CPy_DomainError();
- else
- return CPy_MathRangeError();
- }
- }
- return r;
- }
|