File: float_ops.c

package info (click to toggle)
python-librt 0.6.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 524 kB
  • sloc: ansic: 8,544; cpp: 478; python: 216; makefile: 8
file content (239 lines) | stat: -rw-r--r-- 6,326 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// 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;
}

static double CPy_MathExpectedNonNegativeInputError(double x) {
    char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL);
    if (buf) {
        PyErr_Format(PyExc_ValueError, "expected a nonnegative input, got %s", buf);
        PyMem_Free(buf);
    }
    return CPY_FLOAT_ERROR;
}

static double CPy_MathExpectedPositiveInputError(double x) {
    char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL);
    if (buf) {
        PyErr_Format(PyExc_ValueError, "expected a positive input, got %s", buf);
        PyMem_Free(buf);
    }
    return CPY_FLOAT_ERROR;
}

static double CPy_MathExpectedFiniteInput(double x) {
    char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL);
    if (buf) {
        PyErr_Format(PyExc_ValueError, "expected a finite input, got %s", buf);
        PyMem_Free(buf);
    }
    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)) {
#if CPY_3_14_FEATURES
        return CPy_MathExpectedFiniteInput(x);
#else
        return CPy_DomainError();
#endif
    }
    return v;
}

double CPyFloat_Cos(double x) {
    double v = cos(x);
    if (unlikely(isnan(v)) && !isnan(x)) {
#if CPY_3_14_FEATURES
        return CPy_MathExpectedFiniteInput(x);
#else
        return CPy_DomainError();
#endif
    }
    return v;
}

double CPyFloat_Tan(double x) {
    if (unlikely(isinf(x))) {
#if CPY_3_14_FEATURES
        return CPy_MathExpectedFiniteInput(x);
#else
        return CPy_DomainError();
#endif
    }
    return tan(x);
}

double CPyFloat_Sqrt(double x) {
    if (x < 0.0) {
#if CPY_3_14_FEATURES
        return CPy_MathExpectedNonNegativeInputError(x);
#else
        return CPy_DomainError();
#endif
    }
    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) {
#if CPY_3_14_FEATURES
        return CPy_MathExpectedPositiveInputError(x);
#else
        return CPy_DomainError();
#endif
    }
    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;
}