summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-09-04 02:50:49 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-09-04 02:50:49 (GMT)
commita1c1b0f46865bddd9a84dfae6c146a0e010a4496 (patch)
tree3633fd98894245d55b3f5f22047e5a582704d669
parent37a309db7086381da6ae176cec8817cdd75de872 (diff)
downloadcpython-a1c1b0f46865bddd9a84dfae6c146a0e010a4496.zip
cpython-a1c1b0f46865bddd9a84dfae6c146a0e010a4496.tar.gz
cpython-a1c1b0f46865bddd9a84dfae6c146a0e010a4496.tar.bz2
Introduce new private API function _PyLong_AsScaledDouble. Not used yet,
but will be the foundation for Good Things: + Speed PyLong_AsDouble. + Give PyLong_AsDouble the ability to detect overflow. + Make true division of long/long nearly as accurate as possible (no spurious infinities or NaNs). + Return non-insane results from math.log and math.log10 when passing a long that can't be approximated by a double better than HUGE_VAL.
-rw-r--r--Include/longobject.h9
-rw-r--r--Objects/longobject.c52
2 files changed, 61 insertions, 0 deletions
diff --git a/Include/longobject.h b/Include/longobject.h
index 5d1ea0a..e592891 100644
--- a/Include/longobject.h
+++ b/Include/longobject.h
@@ -18,6 +18,15 @@ extern DL_IMPORT(PyObject *) PyLong_FromUnsignedLong(unsigned long);
extern DL_IMPORT(PyObject *) PyLong_FromDouble(double);
extern DL_IMPORT(long) PyLong_AsLong(PyObject *);
extern DL_IMPORT(unsigned long) PyLong_AsUnsignedLong(PyObject *);
+
+/* _PyLong_AsScaledDouble returns a double x and an exponent e such that
+ the true value is approximately equal to x * 2**(SHIFT*e). e is >= 0.
+ x is 0.0 if and only if the input is 0 (in which case, e and x are both
+ zeroes). Overflow is impossible. Note that the exponent returned must
+ be multiplied by SHIFT! There may not be enough room in an int to store
+ e*SHIFT directly. */
+extern DL_IMPORT(double) _PyLong_AsScaledDouble(PyObject *vv, int *e);
+
extern DL_IMPORT(double) PyLong_AsDouble(PyObject *);
extern DL_IMPORT(PyObject *) PyLong_FromVoidPtr(void *);
extern DL_IMPORT(void *) PyLong_AsVoidPtr(PyObject *);
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 4d4a959..b511928 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -474,6 +474,58 @@ Overflow:
}
+double
+_PyLong_AsScaledDouble(PyObject *vv, int *exponent)
+{
+/* NBITS_WANTED should be > the number of bits in a double's precision,
+ but small enough so that 2**NBITS_WANTED is within the normal double
+ range. nbitsneeded is set to 1 less than that because the most-significant
+ Python digit contains at least 1 significant bit, but we don't want to
+ bother counting them (catering to the worst case cheaply).
+
+ 57 is one more than VAX-D double precision; I (Tim) don't know of a double
+ format with more precision than that; it's 1 larger so that we add in at
+ least one round bit to stand in for the ignored least-significant bits.
+*/
+#define NBITS_WANTED 57
+ PyLongObject *v;
+ double x;
+ const double multiplier = (double)(1L << SHIFT);
+ int i, sign;
+ int nbitsneeded;
+
+ if (vv == NULL || !PyLong_Check(vv)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ v = (PyLongObject *)vv;
+ i = v->ob_size;
+ sign = 1;
+ if (i < 0) {
+ sign = -1;
+ i = -(i);
+ }
+ else if (i == 0) {
+ *exponent = 0;
+ return 0.0;
+ }
+ --i;
+ x = (double)v->ob_digit[i];
+ nbitsneeded = NBITS_WANTED - 1;
+ /* Invariant: i Python digits remain unaccounted for. */
+ while (i > 0 && nbitsneeded > 0) {
+ --i;
+ x = x * multiplier + (double)v->ob_digit[i];
+ nbitsneeded -= SHIFT;
+ }
+ /* There are i digits we didn't shift in. Pretending they're all
+ zeroes, the true value is x * 2**(i*SHIFT). */
+ *exponent = i;
+ assert(x > 0.0);
+ return x * sign;
+#undef NBITS_WANTED
+}
+
/* Get a C double from a long int object. */
double