[Python-checkins] r71468 - python/branches/py3k-short-float-repr/Python/pystrtod.c

mark.dickinson python-checkins at python.org
Sat Apr 11 16:17:15 CEST 2009


Author: mark.dickinson
Date: Sat Apr 11 16:17:15 2009
New Revision: 71468

Log:
Refactor format_float_short, and add some more comments
and checks.


Modified:
   python/branches/py3k-short-float-repr/Python/pystrtod.c

Modified: python/branches/py3k-short-float-repr/Python/pystrtod.c
==============================================================================
--- python/branches/py3k-short-float-repr/Python/pystrtod.c	(original)
+++ python/branches/py3k-short-float-repr/Python/pystrtod.c	Sat Apr 11 16:17:15 2009
@@ -554,22 +554,23 @@
 	char *p;
 	Py_ssize_t bufsize = 0;
 	char *digits, *digits_end;
-	int decpt, sign, exp_len, dec_pos, use_exp = 0;
-	Py_ssize_t n_digits, min_digits = 0;
-	Py_ssize_t min_digits1;
-
-	/* _Py_dg_dtoa returns a digit string (no decimal point or
-	   exponent).  Must be matched by a call to _Py_dg_freedtoa. */
-	digits = _Py_dg_dtoa(d, mode, precision, &decpt, &sign, &digits_end);
+	int decpt_as_int, sign, exp_len, exp = 0, use_exp = 0;
+	Py_ssize_t decpt, digits_len, vdigits_start, vdigits_end;
+
+	/* _Py_dg_dtoa returns a digit string (no decimal point or exponent).
+	   Must be matched by a call to _Py_dg_freedtoa. */
+	digits = _Py_dg_dtoa(d, mode, precision, &decpt_as_int, &sign,
+			     &digits_end);
+	decpt = (Py_ssize_t)decpt_as_int;
 	if (digits == NULL) {
 		/* The only failure mode is no memory. */
 		PyErr_NoMemory();
 		goto exit;
 	}
 	assert(digits_end != NULL && digits_end >= digits);
-	n_digits = digits_end - digits;
+	digits_len = digits_end - digits;
 
-	if (n_digits && !isdigit(digits[0])) {
+	if (digits_len && !isdigit(digits[0])) {
 		/* Infinities and nans here; adapt Gay's output,
 		   so convert Infinity to inf and NaN to nan, and
 		   ignore sign of nan. Then return. */
@@ -609,81 +610,96 @@
 		goto exit;
 	}
 
-	/* We got digits back, format them. */
-
-	/* Detect if we're using exponents or not, and figure out how many
-	   additional digits we need beyond those provided by dtoa. */
+	/* We got digits back, format them.  We may need to pad 'digits'
+	   either on the left or right (or both) with extra zeros, so in
+	   general the resulting string has the form
+
+	     [<sign>]<zeros><digits><zeros>[<exponent>]
+
+	   where either of the <zeros> pieces could be empty, and there's a
+	   decimal point that could appear either in <digits> or in the
+	   leading or trailing <zeros>.
+
+	   Imagine an infinite 'virtual' string vdigits, consisting of the
+	   string 'digits' (starting at index 0) padded on both the left and
+	   right with infinite strings of zeros.  We want to output a slice
+
+	     vdigits[vdigits_start : vdigits_end]
+
+	   of this virtual string.  Thus if vdigits_start < 0 then we'll end
+	   up producing some leading zeros; if vdigits_end > digits_len there
+	   will be trailing zeros in the output.  The next section of code
+	   determines whether to use an exponent or not, figures out the
+	   position 'decpt' of the decimal point, and computes 'vdigits_start'
+	   and 'vdigits_end'. */
+	vdigits_end = digits_len;
 	switch (format_code) {
 	case 'e':
 		use_exp = 1;
-		min_digits = precision;
+		vdigits_end = precision;
 		break;
 	case 'f':
-		min_digits = decpt + precision;
+		vdigits_end = decpt + precision;
 		break;
 	case 'g':
 		if (decpt <= -4 || decpt > precision)
 			use_exp = 1;
-		/* assumes that not both of add_dot_0_if_integer and
-		   use_alt_formatting are set */
-		else if (add_dot_0_if_integer)
-			min_digits = decpt + 1;
 		if (use_alt_formatting)
-			min_digits = precision;
+			vdigits_end = precision;
 		break;
 	case 'r':
+		/* convert to exponential format at 1e16.  We used to convert
+		   at 1e17, but that gives odd-looking results for some values
+		   when a 16-digit 'shortest' repr is padded with bogus zeros.
+		   For example, repr(2e16+8) would give 20000000000000010.0;
+		   the true value is 20000000000000008.0. */
 		if (decpt <= -4 || decpt > 16)
 			use_exp = 1;
-		else if (add_dot_0_if_integer)
-			min_digits = decpt + 1;
 		break;
 	case 's':
 		/* if we're forcing a digit after the point, convert to
 		   exponential format at 1e11.  If not, convert at 1e12. */
-		if (decpt <= -4 ||
-		    decpt > precision - (add_dot_0_if_integer != 0))
+		if (decpt <= -4 || decpt >
+		    (add_dot_0_if_integer ? precision-1 : precision))
 			use_exp = 1;
-		else if (add_dot_0_if_integer)
-			min_digits = decpt + 1;
 		break;
 	default:
 		PyErr_BadInternalCall();
 		goto exit;
 	}
 
-	/* dec_pos = position of decimal point in buffer */
-	if (use_exp)
-		dec_pos = 1;
+	/* if using an exponent, reset decimal point position to 1 and adjust
+	   exponent accordingly.*/
+	if (use_exp) {
+		exp = decpt - 1;
+		decpt = 1;
+	}
+	/* ensure vdigits_start < decpt <= vdigits_end, or vdigits_start <
+	   decpt < vdigits_end if add_dot_0_if_integer and no exponent */
+	vdigits_start = decpt <= 0 ? decpt-1 : 0;
+	if (!use_exp && add_dot_0_if_integer)
+		vdigits_end = vdigits_end > decpt ? vdigits_end : decpt + 1;
 	else
-		dec_pos = decpt;
+		vdigits_end = vdigits_end > decpt ? vdigits_end : decpt;
 
-	min_digits -= n_digits;
-	min_digits1 = (n_digits < dec_pos) ?
-		(min_digits - (dec_pos-n_digits)) : min_digits;
+	/* double check inequalities */
+	assert(vdigits_start <= 0 &&
+	       0 <= digits_len &&
+	       digits_len <= vdigits_end);
+	/* decimal point should be in (vdigits_start, vdigits_end] */
+	assert(vdigits_start < decpt && decpt <= vdigits_end);
 
 	/* Compute an upper bound how much memory we need. This might be a few
 	   chars too long, but no big deal. */
 	bufsize =
-		/* 1: Sign */
-		1 +
-
-		/* 2: Zero padding on left of digit string */
-		(dec_pos <= 0 ? -dec_pos + 2 : 0) +
-
-		/* 3: Digits, with included decimal point */
-		(1 + n_digits) +
+		/* sign, decimal point and trailing 0 byte */
+		3 +
 
-		/* 4: And zeros on the right up to the decimal point */
-		((n_digits < dec_pos) ? dec_pos-n_digits + 1 : 0) +
+		/* total digit count (including zero padding on both sides) */
+		(vdigits_end - vdigits_start) +
 
-		/* 5: And more trailing zeros when necessary */
-		(min_digits1 > 0 ? min_digits1 : 0) +
-
-		/* 6: Exponent "e+100", max 3 numerical digits */
-		(use_exp ? 5 : 0) +
-
-		/* Trailing 0 byte */
-		1;
+		/* exponent "e+100", max 3 numerical digits */
+		(use_exp ? 5 : 0);
 
 	/* Now allocate the memory and initialize p to point to the start of
 	   it. */
@@ -701,39 +717,45 @@
 	else if (always_add_sign)
 		*p++ = '+';
 
+	/* note that exactly one of the three 'if' conditions is true,
+	   so we include exactly one decimal point */
 	/* 2: Zero padding on left of digit string */
-	if (dec_pos <= 0) {
-		*p++ = '0';
+	if (decpt <= 0) {
+		memset(p, '0', decpt-vdigits_start);
+		p += decpt - vdigits_start;
 		*p++ = '.';
-		memset(p, '0', -dec_pos);
-		p -= dec_pos;
+		memset(p, '0', 0-decpt);
+		p += 0-decpt;
+	}
+	else {
+		memset(p, '0', 0-vdigits_start);
+		p += 0 - vdigits_start;
 	}
 
 	/* 3: Digits, with included decimal point */
-	if (0 < dec_pos && dec_pos <= n_digits) {
-		strncpy(p, digits, dec_pos);
-		p += dec_pos;
+	if (0 < decpt && decpt <= digits_len) {
+		strncpy(p, digits, decpt-0);
+		p += decpt-0;
 		*p++ = '.';
-		strncpy(p, digits+dec_pos, n_digits-dec_pos);
-		p += n_digits-dec_pos;
+		strncpy(p, digits+decpt, digits_len-decpt);
+		p += digits_len-decpt;
 	}
 	else {
-		strncpy(p, digits, n_digits);
-		p += n_digits;
+		strncpy(p, digits, digits_len);
+		p += digits_len;
 	}
 
-	/* 4: And zeros on the right up to the decimal point */
-	if (n_digits < dec_pos) {
-		memset(p, '0', dec_pos-n_digits);
-		p += dec_pos-n_digits;
+	/* 4: And zeros on the right */
+	if (digits_len < decpt) {
+		memset(p, '0', decpt-digits_len);
+		p += decpt-digits_len;
 		*p++ = '.';
-		min_digits -= dec_pos-n_digits;
+		memset(p, '0', vdigits_end-decpt);
+		p += vdigits_end-decpt;
 	}
-
-	/* 5: And more trailing zeros when necessary */
-	if (min_digits > 0) {
-		memset(p, '0', min_digits);
-		p += min_digits;
+	else {
+		memset(p, '0', vdigits_end-digits_len);
+		p += vdigits_end-digits_len;
 	}
 
 	/* Delete a trailing decimal pt unless using alternative formatting. */
@@ -743,7 +765,7 @@
 	/* 6: Now that we've done zero padding, add an exponent if needed. */
 	if (use_exp) {
 		*p++ = float_strings[OFS_E][0];
-		exp_len = sprintf(p, "%+.02d", decpt-1);
+		exp_len = sprintf(p, "%+.02d", exp);
 		p += exp_len;
 	}
   exit:


More information about the Python-checkins mailing list