[Numpy-svn] r6227 - branches/fix_float_format/numpy/core/src

numpy-svn@scip... numpy-svn@scip...
Sun Dec 28 21:21:36 CST 2008


Author: cdavid
Date: 2008-12-28 21:21:33 -0600 (Sun, 28 Dec 2008)
New Revision: 6227

Modified:
   branches/fix_float_format/numpy/core/src/npy_format.c
Log:
Improve NumPyOS_ascii_format: handle nan and inf.

Modified: branches/fix_float_format/numpy/core/src/npy_format.c
===================================================================
--- branches/fix_float_format/numpy/core/src/npy_format.c	2008-12-29 03:21:03 UTC (rev 6226)
+++ branches/fix_float_format/numpy/core/src/npy_format.c	2008-12-29 03:21:33 UTC (rev 6227)
@@ -152,42 +152,20 @@
 		}
 	}
 }
-/**
- * NumPyOS_ascii_formatd:
- * @buffer: A buffer to place the resulting string in
- * @buf_size: The length of the buffer.
- * @format: The printf()-style format to use for the
- *          code to use for converting. 
- * @d: The #gdouble to convert
- *
- * This is similar to PyOS_ascii_formatd in python > 2.6, except that it does
- * not handle 'n'.
- *
- * Converts a #gdouble to a string, using the '.' as
- * decimal point. To format the number you pass in
- * a printf()-style format string. Allowed conversion
- * specifiers are 'e', 'E', 'f', 'F', 'g', 'G'.
- * 
- * 'Z' is the same as 'g', except it always has a decimal and
- *     at least one digit after the decimal.
- *
- * Return value: The pointer to the buffer with the converted string.
- **/
-char* NumPyOS_ascii_formatd(char *buffer, size_t buf_size, const char *format, 
-		   	    double val)
+
+/*
+ * Check that the format string is a valid one for NumPyOS_ascii_format*
+ */
+static int _check_ascii_format(const char *format)
 {
 	char format_char;
 	size_t format_len = strlen(format);
 
-	/* Issue 2264: code 'Z' requires copying the format.  'Z' is 'g', but
-	   also with at least one character past the decimal. */
-	char tmp_format[FLOAT_FORMATBUFLEN];
-
 	/* The last character in the format string must be the format char */
 	format_char = format[format_len - 1];
 
 	if (format[0] != '%') {
-		return NULL;
+		return -1;
 	}
 
 	/* I'm not sure why this test is here.  It's ensuring that the format
@@ -195,7 +173,7 @@
 	   lowercase l, or a percent. This is the reverse of the commented-out
 	   test about 10 lines ago. */
 	if (strpbrk(format + 1, "'l%")) {
-		return NULL;
+		return -1;
 	}
 
 	/* Also curious about this function is that it accepts format strings
@@ -205,204 +183,91 @@
 
 	if (!(format_char == 'e' || format_char == 'E' || 
 	      format_char == 'f' || format_char == 'F' || 
-	      format_char == 'g' || format_char == 'G' ||
-	      format_char == 'Z')) {
-		return NULL;
+	      format_char == 'g' || format_char == 'G')) {
+		return -1;
 	}
 
-	/* Map 'Z' format_char to 'g', by copying the format string and
-	   replacing the final char with a 'g' */
-	if (format_char == 'Z') {
-		if (format_len + 1 >= sizeof(tmp_format)) {
-			/* The format won't fit in our copy.  Error out.  In
-			   practice, this will never happen and will be
-			   detected by returning NULL */
-			return NULL;
-		}
-		strcpy(tmp_format, format);
-		tmp_format[format_len - 1] = 'g';
-		format = tmp_format;
-	}
-
-
-	/* Have PyOS_snprintf do the hard work */
-	PyOS_snprintf(buffer, buf_size, format, val);
-
-	/* Do various fixups on the return string */
-
-	/* Get the current locale, and find the decimal point string.
-	   Convert that string back to a dot. */
-	change_decimal_from_locale_to_dot(buffer);
-
-	/* If an exponent exists, ensure that the exponent is at least
-	   MIN_EXPONENT_DIGITS digits, providing the buffer is large enough
-	   for the extra zeros.  Also, if there are more than
-	   MIN_EXPONENT_DIGITS, remove as many zeros as possible until we get
-	   back to MIN_EXPONENT_DIGITS */
-	ensure_minumim_exponent_length(buffer, buf_size);
-
-	/* If format_char is 'Z', make sure we have at least one character
-	   after the decimal point (and make sure we have a decimal point). */
-	if (format_char == 'Z') {
-		ensure_decimal_point(buffer, buf_size);
-	}
-	ensure_decimal_point(buffer, buf_size);
-
-	return buffer;
+	return 0;
 }
 
-char* NumPyOS_ascii_formatf(char *buffer, size_t buf_size, const char *format, 
-		   	    float val)
+/*
+ * Fix the generated string: make sure the decimal is ., that exponent has a
+ * minimal number of digits, and that it has a decimal + one digit after that
+ * decimal if decimal argument != 0 (Same effect that 'Z' format in
+ * PyOS_ascii_formatd
+ */
+static char* _fix_ascii_format(char* buf, size_t buflen, int decimal)
 {
-	char format_char;
-	size_t format_len = strlen(format);
-
-	/* Issue 2264: code 'Z' requires copying the format.  'Z' is 'g', but
-	   also with at least one character past the decimal. */
-	char tmp_format[FLOAT_FORMATBUFLEN];
-
-	/* The last character in the format string must be the format char */
-	format_char = format[format_len - 1];
-
-	if (format[0] != '%') {
-		return NULL;
-	}
-
-	/* I'm not sure why this test is here.  It's ensuring that the format
-	   string after the first character doesn't have a single quote, a
-	   lowercase l, or a percent. This is the reverse of the commented-out
-	   test about 10 lines ago. */
-	if (strpbrk(format + 1, "'l%")) {
-		return NULL;
-	}
-
-	/* Also curious about this function is that it accepts format strings
-	   like "%xg", which are invalid for floats.  In general, the
-	   interface to this function is not very good, but changing it is
-	   difficult because it's a public API. */
-
-	if (!(format_char == 'e' || format_char == 'E' || 
-	      format_char == 'f' || format_char == 'F' || 
-	      format_char == 'g' || format_char == 'G' ||
-	      format_char == 'Z')) {
-		return NULL;
-	}
-
-	/* Map 'Z' format_char to 'g', by copying the format string and
-	   replacing the final char with a 'g' */
-	if (format_char == 'Z') {
-		if (format_len + 1 >= sizeof(tmp_format)) {
-			/* The format won't fit in our copy.  Error out.  In
-			   practice, this will never happen and will be
-			   detected by returning NULL */
-			return NULL;
-		}
-		strcpy(tmp_format, format);
-		tmp_format[format_len - 1] = 'g';
-		format = tmp_format;
-	}
-
-
-	/* Have PyOS_snprintf do the hard work */
-	PyOS_snprintf(buffer, buf_size, format, val);
-
-	/* Do various fixups on the return string */
-
 	/* Get the current locale, and find the decimal point string.
 	   Convert that string back to a dot. */
-	change_decimal_from_locale_to_dot(buffer);
+	change_decimal_from_locale_to_dot(buf);
 
 	/* If an exponent exists, ensure that the exponent is at least
 	   MIN_EXPONENT_DIGITS digits, providing the buffer is large enough
 	   for the extra zeros.  Also, if there are more than
 	   MIN_EXPONENT_DIGITS, remove as many zeros as possible until we get
 	   back to MIN_EXPONENT_DIGITS */
-	ensure_minumim_exponent_length(buffer, buf_size);
+	ensure_minumim_exponent_length(buf, buflen);
 
-	/* If format_char is 'Z', make sure we have at least one character
-	   after the decimal point (and make sure we have a decimal point). */
-	if (format_char == 'Z') {
-		ensure_decimal_point(buffer, buf_size);
+	if (decimal != 0) {
+		ensure_decimal_point(buf, buflen);
 	}
-	ensure_decimal_point(buffer, buf_size);
 
-	return buffer;
+	return buf;
 }
 
-char* NumPyOS_ascii_formatl(char *buffer, size_t buf_size, const char *format, 
-		   	    long double val)
-{
-	char format_char;
-	size_t format_len = strlen(format);
-
-	/* Issue 2264: code 'Z' requires copying the format.  'Z' is 'g', but
-	   also with at least one character past the decimal. */
-	char tmp_format[FLOAT_FORMATBUFLEN];
-
-	/* The last character in the format string must be the format char */
-	format_char = format[format_len - 1];
-
-	if (format[0] != '%') {
-		return NULL;
-	}
-
-	/* I'm not sure why this test is here.  It's ensuring that the format
-	   string after the first character doesn't have a single quote, a
-	   lowercase l, or a percent. This is the reverse of the commented-out
-	   test about 10 lines ago. */
-	if (strpbrk(format + 1, "'l%")) {
-		return NULL;
-	}
-
-	/* Also curious about this function is that it accepts format strings
-	   like "%xg", which are invalid for floats.  In general, the
-	   interface to this function is not very good, but changing it is
-	   difficult because it's a public API. */
-
-	if (!(format_char == 'e' || format_char == 'E' || 
-	      format_char == 'f' || format_char == 'F' || 
-	      format_char == 'g' || format_char == 'G' ||
-	      format_char == 'Z')) {
-		return NULL;
-	}
-
-	/* Map 'Z' format_char to 'g', by copying the format string and
-	   replacing the final char with a 'g' */
-	if (format_char == 'Z') {
-		if (format_len + 1 >= sizeof(tmp_format)) {
-			/* The format won't fit in our copy.  Error out.  In
-			   practice, this will never happen and will be
-			   detected by returning NULL */
-			return NULL;
-		}
-		strcpy(tmp_format, format);
-		tmp_format[format_len - 1] = 'g';
-		format = tmp_format;
-	}
-
-
-	/* Have PyOS_snprintf do the hard work */
-	PyOS_snprintf(buffer, buf_size, format, val);
-
-	/* Do various fixups on the return string */
-
-	/* Get the current locale, and find the decimal point string.
-	   Convert that string back to a dot. */
-	change_decimal_from_locale_to_dot(buffer);
-
-	/* If an exponent exists, ensure that the exponent is at least
-	   MIN_EXPONENT_DIGITS digits, providing the buffer is large enough
-	   for the extra zeros.  Also, if there are more than
-	   MIN_EXPONENT_DIGITS, remove as many zeros as possible until we get
-	   back to MIN_EXPONENT_DIGITS */
-	ensure_minumim_exponent_length(buffer, buf_size);
-
-	/* If format_char is 'Z', make sure we have at least one character
-	   after the decimal point (and make sure we have a decimal point). */
-	if (format_char == 'Z') {
-		ensure_decimal_point(buffer, buf_size);
-	}
-	ensure_decimal_point(buffer, buf_size);
-
-	return buffer;
+/*
+ * NumPyOS_ascii_format*:
+ * 	- buffer: A buffer to place the resulting string in
+ * 	- buf_size: The length of the buffer.
+ * 	- format: The printf()-style format to use for the code to use for
+ * 	converting. 
+ * 	- value: The value to convert
+ * 	- decimal: if != 0, always has a decimal, and at leasat one digit after
+ * 	the decimal. This has the same effect as passing 'Z' in the origianl
+ * 	PyOS_ascii_formatd
+ *
+ * This is similar to PyOS_ascii_formatd in python > 2.6, except that it does
+ * not handle 'n', and handles nan / inf.
+ *
+ * Converts a #gdouble to a string, using the '.' as decimal point. To format
+ * the number you pass in a printf()-style format string. Allowed conversion
+ * specifiers are 'e', 'E', 'f', 'F', 'g', 'G'.
+ * 
+ * Return value: The pointer to the buffer with the converted string.
+ */
+#define _ASCII_FORMAT(type, suffix) \
+char* NumPyOS_ascii_format ## suffix(char *buffer, size_t buf_size, \
+			      const char *format, \
+			      type val, int decimal) \
+{\
+	if (isfinite(val)) { \
+		if(_check_ascii_format(format)) {\
+			return NULL; \
+		} \
+		PyOS_snprintf(buffer, buf_size, format, val); \
+		return _fix_ascii_format(buffer, buf_size, decimal);\
+	} else if (isnan(val)){ \
+		if (buf_size < 4) { \
+			return NULL; \
+		} \
+		strncpy(buffer, "nan", 4); \
+	} else { \
+		if (signbit(val)) { \
+			if (buf_size < 5) { \
+				return NULL; \
+			} \
+			strncpy(buffer, "-inf", 5); \
+		} else { \
+			if (buf_size < 4) { \
+				return NULL; \
+			} \
+			strncpy(buffer, "inf", 4); \
+		} \
+	} \
+	return buffer; \
 }
+
+_ASCII_FORMAT(float, f)
+_ASCII_FORMAT(double, d)
+_ASCII_FORMAT(long double, l)



More information about the Numpy-svn mailing list