--- Objects/stringobject2.6b1.c 2008-06-10 14:23:22.000000000 -0700 +++ Objects/stringobject.c 2008-06-22 16:49:25.000000000 -0700 @@ -5,6 +5,11 @@ #include "Python.h" #include +#include /* DBL_DIG, LDBL_DIG */ +#ifndef LDBL_DIG /* no long double? */ +# define LDBL_DIG 0 +#endif + #ifdef COUNT_ALLOCS int null_strings, one_strings; #endif @@ -158,10 +163,11 @@ PyString_FromFormatV(const char *format, va_list vargs) { va_list count; - Py_ssize_t n = 0; + Py_ssize_t i, w, n = 0; const char* f; char *s; PyObject* string; + int longflag, size_tflag; #ifdef VA_LIST_IS_ARRAY Py_MEMCPY(count, vargs, sizeof(va_list)); @@ -175,17 +181,24 @@ /* step 1: figure out how large a buffer we need */ for (f = format; *f; f++) { if (*f == '%') { - const char* p = f; - while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f))) - ; - - /* skip the 'l' or 'z' in {%ld, %zd, %lu, %zu} since - * they don't affect the amount of space we reserve. - */ - if ((*f == 'l' || *f == 'z') && - (f[1] == 'd' || f[1] == 'u')) - ++f; - + const char* p = f++; + while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f))) + f++; + longflag = size_tflag = 0; + if (*f == 'l' || *f == 'z') { + if (*f++ == 'l') + longflag = 1; + else + size_tflag = 1; + if (*f != 'd' && *f != 'u') + f--; + } +#if LDBL_DIG > 0 + else if (*f == 'L' && f[1] == 'f') { + longflag = 1; + f++; + } +#endif switch (*f) { case 'c': (void)va_arg(count, int); @@ -194,12 +207,33 @@ n++; break; case 'd': case 'u': case 'i': case 'x': - (void) va_arg(count, int); - /* 20 bytes is enough to hold a 64-bit + if (longflag) + (void) va_arg(count, long); + else if (size_tflag) + (void) va_arg(count, size_t); + else + (void) va_arg(count, int); + /* 20 bytes is enough to hold any 64-bit integer. Decimal takes the most space. This isn't enough for octal. */ n += 20; break; + case 'f': + /* assume %f produces at most (L)DBL_DIG + digits before and after the decimal + point, plus the latter plus a sign */ +#if LDBL_DIG > 0 + if (longflag) { + (void) va_arg(count, long double); + n += LDBL_DIG + 2 + LDBL_DIG; + } + else +#endif + { + (void) va_arg(count, double); + n += DBL_DIG + 2 + DBL_DIG; + } + break; case 's': s = va_arg(count, char*); n += strlen(s); @@ -239,34 +273,36 @@ for (f = format; *f; f++) { if (*f == '%') { const char* p = f++; - Py_ssize_t i; - int longflag = 0; - int size_tflag = 0; - /* parse the width.precision part (we're only - interested in the precision value, if any) */ - n = 0; + int precision = 0; + /* parse width.precision, if any */ + w = n = 0; while (isdigit(Py_CHARMASK(*f))) - n = (n*10) + *f++ - '0'; + w = (w*10) + *f++ - '0'; if (*f == '.') { f++; - n = 0; while (isdigit(Py_CHARMASK(*f))) n = (n*10) + *f++ - '0'; + precision = 1; } + /* skip any non-alpha flags */ while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f))) f++; - /* handle the long flag, but only for %ld and %lu. - others can be added when necessary. */ - if (*f == 'l' && (f[1] == 'd' || f[1] == 'u')) { - longflag = 1; - ++f; + /* handle the long flag and size_t flags */ + longflag = size_tflag = 0; + if (*f == 'l' || *f == 'z') { + if (*f++ == 'l') + longflag = 1; + else + size_tflag = 1; + if (*f != 'd' && *f != 'u') + f--; } - /* handle the size_t flag. */ - if (*f == 'z' && (f[1] == 'd' || f[1] == 'u')) { - size_tflag = 1; - ++f; +#if LDBL_DIG > 0 + else if (*f == 'L' && f[1] == 'f') { + longflag = 1; + f++; } - +#endif switch (*f) { case 'c': *s++ = va_arg(vargs, int); @@ -284,13 +320,39 @@ case 'u': if (longflag) sprintf(s, "%lu", - va_arg(vargs, unsigned long)); + va_arg(vargs, unsigned long)); else if (size_tflag) sprintf(s, "%" PY_FORMAT_SIZE_T "u", va_arg(vargs, size_t)); else sprintf(s, "%u", - va_arg(vargs, unsigned int)); + va_arg(vargs, unsigned int)); + s += strlen(s); + break; + case 'f': + if (precision) { +#if LDBL_DIG > 0 + if (longflag) { + if (n > LDBL_DIG) + n = LDBL_DIG; + sprintf(s, "%.*Lf", (int)n, + va_arg(vargs, long double)); + } + else +#endif + { + if (n > DBL_DIG) + n = DBL_DIG; + sprintf(s, "%.*f", (int)n, + va_arg(vargs, double)); + } + } +#if LDBL_DIG > 0 + else if (longflag) + sprintf(s, "%Lf", va_arg(vargs, long double)); +#endif + else + sprintf(s, "%f", va_arg(vargs, double)); s += strlen(s); break; case 'i': @@ -306,6 +368,8 @@ i = strlen(p); if (n > 0 && i > n) i = n; + else if (w > 0 && i > w) + i = w; Py_MEMCPY(s, p, i); s += i; break; @@ -334,7 +398,7 @@ } end: - _PyString_Resize(&string, s - PyString_AS_STRING(string)); + _PyString_Resize(&string, (Py_ssize_t)(s - PyString_AS_STRING(string))); return string; } --- Doc/c-api/exceptions2.6b1.rst 2008-04-26 19:28:02.000000000 -0700 +++ Doc/c-api/exceptions.rst 2008-06-22 22:08:55.000000000 -0700 @@ -154,6 +154,8 @@ .. % because not all compilers support the %z width modifier -- we fake it .. % when necessary via interpolating PY_FORMAT_SIZE_T. .. % %u, %lu, %zu should have "new in Python 2.5" blurbs. + .. % %Lf is only available on platforms supporting C type long double. + .. % a precision specification is supported for the %f, %Lf and %s formats. +-------------------+---------------+--------------------------------+ | Format Characters | Type | Comment | @@ -187,6 +189,12 @@ | :attr:`%x` | int | Exactly equivalent to | | | | ``printf("%x")``. | +-------------------+---------------+--------------------------------+ + | :attr:`%f` | double | Exactly equivalent to | + | | | ``printf("%f")``. | + +-------------------+---------------+--------------------------------+ + | :attr:`%Lf` | long double | Exactly equivalent to | + | | | ``printf("%Lf")``. | + +-------------------+---------------+--------------------------------+ | :attr:`%s` | char\* | A null-terminated C character | | | | array. | +-------------------+---------------+--------------------------------+ --- Doc/c-api/string2.6b1.rst 2008-06-22 22:03:10.000000000 -0700 +++ Doc/c-api/string.rst 2008-06-22 22:05:20.000000000 -0700 @@ -73,6 +73,8 @@ .. % because not all compilers support the %z width modifier -- we fake it .. % when necessary via interpolating PY_FORMAT_SIZE_T. .. % %u, %lu, %zu should have "new in Python 2.5" blurbs. + .. % %Lf is only available on platforms supporting C type long double. + .. % a precision specification is supported for the %f, %Lf and %s formats. +-------------------+---------------+--------------------------------+ | Format Characters | Type | Comment | @@ -106,6 +108,12 @@ | :attr:`%x` | int | Exactly equivalent to | | | | ``printf("%x")``. | +-------------------+---------------+--------------------------------+ + | :attr:`%f` | double | Exactly equivalent to | + | | | ``printf("%f")``. | + +-------------------+---------------+--------------------------------+ + | :attr:`%Lf` | long double | Exactly equivalent to | + | | | ``printf("%Lf")``. | + +-------------------+---------------+--------------------------------+ | :attr:`%s` | char\* | A null-terminated C character | | | | array. | +-------------------+---------------+--------------------------------+ --- Modules/_testcapimodule2.6b1.c 2008-06-22 22:47:04.000000000 -0700 +++ Modules/_testcapimodule.c 2008-06-22 22:59:36.000000000 -0700 @@ -696,18 +696,23 @@ test_string_from_format(PyObject *self, PyObject *args) { PyObject *result; - char *msg; + char msg[256], *got; -#define CHECK_1_FORMAT(FORMAT, TYPE) \ - result = PyString_FromFormat(FORMAT, (TYPE)1); \ - if (result == NULL) \ - return NULL; \ - if (strcmp(PyString_AsString(result), "1")) { \ - msg = FORMAT " failed at 1"; \ - goto Fail; \ - } \ +#define CHECK_FORMAT(FORMAT, TYPE, VALUE, EXPECTED) \ + result = PyString_FromFormat(FORMAT, (TYPE)VALUE); \ + if (result == NULL) \ + return NULL; \ + got = PyString_AsString(result); \ + if (strcmp(got, EXPECTED)) { \ + sprintf(msg, "%s failed: got %s, expected %s", \ + FORMAT, got, EXPECTED); \ + goto Fail; \ + } \ Py_DECREF(result) +#define CHECK_1_FORMAT(FORMAT, TYPE) \ + CHECK_FORMAT(FORMAT, TYPE, 1, "1") + CHECK_1_FORMAT("%d", int); CHECK_1_FORMAT("%ld", long); /* The z width modifier was added in Python 2.5. */ @@ -718,12 +723,18 @@ CHECK_1_FORMAT("%lu", unsigned long); CHECK_1_FORMAT("%zu", size_t); + /* The f type code was added in Python 2.7. */ + CHECK_FORMAT("%f", double, 12345.67890, "12345.678900"); + CHECK_FORMAT("%.5f", double, 12345.67890, "12345.67890"); + CHECK_FORMAT("%.2f", double, 12345.67890, "12345.68"); + Py_RETURN_NONE; Fail: Py_XDECREF(result); return raiseTestError("test_string_from_format", msg); +#undef CHECK_FORMAT #undef CHECK_1_FORMAT }