[capi-sig] String to number conversion

Hrvoje Niksic hniksic at xemacs.org
Wed May 7 17:27:15 CEST 2008


I miss a function able to convert the string representation of a
Python primitive number (int, long, float, complex) to an actual
number, basically the reverse of repr for numbers.  The only option I
know of that doesn't involve inspecting the individual digits is eval,
which doesn't cut it -- besides the obvious security implications, it
tends to be slow when used en masse.  What I have in mind is something
along the lines of:

>>> read_number('1')
1
>>> read_number('1.')
1.0
>>> read_number('3.14')
3.1400000000000001
>>> read_number('1000000000000000000')
1000000000000000000L
>>> read_number('2+3j')
(2+3j)
>>> read_number('1e100')
1e+100
>>> read_number('bla')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid numeric value 'bla'

A sample implementation, a variation of which I use in an in-house
project, follows.  It does inspect the string to determine its type,
but does so in a simple C loop which is quite fast for numeric input.
It leaves the actual conversion to the Python's API code that knows
what it's doing.  Because of that it's simple and fast, but still (to
my knowledge) correct.

PyObject *
read_number(PyObject *str)
{
  int isfloat = 0, iscomplex = 0;

  /* First, intuit the type based on characters that appear in the
     string. */
  const char *s = PyString_AS_STRING(str);
  const char *end = s + PyString_GET_SIZE(str);
  const char *q;
  for (q = s; q < end; q++) {
    char c = *q;
    if (c == '.' || c == 'e')
      isfloat = 1;
    else if (c == 'j')
      iscomplex = 1;
    else if (c != '-' && c != '+' && !isdigit(c) && !isspace(c)) {
      PyErr_Format(PyExc_ValueError, "invalid numeric value '%s'", s);
      return NULL;
    }
  }

  /* Now that the type is known, construct the number, leaving the
     actual error checking to the constructors. */
  if (iscomplex)
    return PyObject_CallFunctionObjArgs((PyObject *) &PyComplex_Type, str);
  else if (isfloat)
    return PyFloat_FromString(str, NULL);
  else
    /* handles ints and longs */
    return PyInt_FromString((char *) s, NULL, 10);
}

Would anyone else find this kind of function useful?


More information about the capi-sig mailing list