summaryrefslogtreecommitdiff
path: root/Modules/_decimal/_decimal.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_decimal/_decimal.c')
-rw-r--r--Modules/_decimal/_decimal.c113
1 files changed, 108 insertions, 5 deletions
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c
index e15941aec8..fcc1f151cf 100644
--- a/Modules/_decimal/_decimal.c
+++ b/Modules/_decimal/_decimal.c
@@ -36,7 +36,6 @@
#include <stdlib.h>
#include "docstrings.h"
-#include "memory.h"
#if !defined(MPD_VERSION_HEX) || MPD_VERSION_HEX < 0x02040100
@@ -1890,12 +1889,13 @@ is_space(enum PyUnicode_Kind kind, void *data, Py_ssize_t pos)
/* Return the ASCII representation of a numeric Unicode string. The numeric
string may contain ascii characters in the range [1, 127], any Unicode
space and any unicode digit. If strip_ws is true, leading and trailing
- whitespace is stripped.
+ whitespace is stripped. If ignore_underscores is true, underscores are
+ ignored.
Return NULL if malloc fails and an empty string if invalid characters
are found. */
static char *
-numeric_as_ascii(const PyObject *u, int strip_ws)
+numeric_as_ascii(const PyObject *u, int strip_ws, int ignore_underscores)
{
enum PyUnicode_Kind kind;
void *data;
@@ -1930,6 +1930,9 @@ numeric_as_ascii(const PyObject *u, int strip_ws)
for (; j < len; j++) {
ch = PyUnicode_READ(kind, data, j);
+ if (ignore_underscores && ch == '_') {
+ continue;
+ }
if (0 < ch && ch <= 127) {
*cp++ = ch;
continue;
@@ -2012,7 +2015,7 @@ PyDecType_FromUnicode(PyTypeObject *type, const PyObject *u,
PyObject *dec;
char *s;
- s = numeric_as_ascii(u, 0);
+ s = numeric_as_ascii(u, 0, 0);
if (s == NULL) {
return NULL;
}
@@ -2032,7 +2035,7 @@ PyDecType_FromUnicodeExactWS(PyTypeObject *type, const PyObject *u,
PyObject *dec;
char *s;
- s = numeric_as_ascii(u, 1);
+ s = numeric_as_ascii(u, 1, 1);
if (s == NULL) {
return NULL;
}
@@ -3394,6 +3397,105 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
return (PyObject *) pylong;
}
+/* Convert a Decimal to its exact integer ratio representation. */
+static PyObject *
+dec_as_integer_ratio(PyObject *self, PyObject *args UNUSED)
+{
+ PyObject *numerator = NULL;
+ PyObject *denominator = NULL;
+ PyObject *exponent = NULL;
+ PyObject *result = NULL;
+ PyObject *tmp;
+ mpd_ssize_t exp;
+ PyObject *context;
+ uint32_t status = 0;
+
+ if (mpd_isspecial(MPD(self))) {
+ if (mpd_isnan(MPD(self))) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot convert NaN to integer ratio");
+ }
+ else {
+ PyErr_SetString(PyExc_OverflowError,
+ "cannot convert Infinity to integer ratio");
+ }
+ return NULL;
+ }
+
+ CURRENT_CONTEXT(context);
+
+ tmp = dec_alloc();
+ if (tmp == NULL) {
+ return NULL;
+ }
+
+ if (!mpd_qcopy(MPD(tmp), MPD(self), &status)) {
+ Py_DECREF(tmp);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ exp = mpd_iszero(MPD(tmp)) ? 0 : MPD(tmp)->exp;
+ MPD(tmp)->exp = 0;
+
+ /* context and rounding are unused here: the conversion is exact */
+ numerator = dec_as_long(tmp, context, MPD_ROUND_FLOOR);
+ Py_DECREF(tmp);
+ if (numerator == NULL) {
+ goto error;
+ }
+
+ exponent = PyLong_FromSsize_t(exp < 0 ? -exp : exp);
+ if (exponent == NULL) {
+ goto error;
+ }
+
+ tmp = PyLong_FromLong(10);
+ if (tmp == NULL) {
+ goto error;
+ }
+
+ Py_SETREF(exponent, _py_long_power(tmp, exponent, Py_None));
+ Py_DECREF(tmp);
+ if (exponent == NULL) {
+ goto error;
+ }
+
+ if (exp >= 0) {
+ Py_SETREF(numerator, _py_long_multiply(numerator, exponent));
+ if (numerator == NULL) {
+ goto error;
+ }
+ denominator = PyLong_FromLong(1);
+ if (denominator == NULL) {
+ goto error;
+ }
+ }
+ else {
+ denominator = exponent;
+ exponent = NULL;
+ tmp = _PyLong_GCD(numerator, denominator);
+ if (tmp == NULL) {
+ goto error;
+ }
+ Py_SETREF(numerator, _py_long_floor_divide(numerator, tmp));
+ Py_SETREF(denominator, _py_long_floor_divide(denominator, tmp));
+ Py_DECREF(tmp);
+ if (numerator == NULL || denominator == NULL) {
+ goto error;
+ }
+ }
+
+ result = PyTuple_Pack(2, numerator, denominator);
+
+
+error:
+ Py_XDECREF(exponent);
+ Py_XDECREF(denominator);
+ Py_XDECREF(numerator);
+ return result;
+}
+
static PyObject *
PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds)
{
@@ -4702,6 +4804,7 @@ static PyMethodDef dec_methods [] =
/* Miscellaneous */
{ "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float },
{ "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple },
+ { "as_integer_ratio", dec_as_integer_ratio, METH_NOARGS, doc_as_integer_ratio },
/* Special methods */
{ "__copy__", dec_copy, METH_NOARGS, NULL },