mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	Issue 5780: Fix test_float failures for legacy style float repr.
This commit is contained in:
		
							parent
							
								
									7efad9ec5a
								
							
						
					
					
						commit
						3370cce4dd
					
				
					 2 changed files with 65 additions and 12 deletions
				
			
		
							
								
								
									
										12
									
								
								Misc/NEWS
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								Misc/NEWS
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -20,14 +20,7 @@ Core and Builtins
 | 
			
		|||
- Implement PEP 378, Format Specifier for Thousands Separator, for
 | 
			
		||||
  floats.
 | 
			
		||||
 | 
			
		||||
- The repr function switches to exponential notation at 1e16, not 1e17
 | 
			
		||||
  as it did before.  This change applies to both 'short' and legacy
 | 
			
		||||
  float repr styles.  For the new repr style, it avoids misleading
 | 
			
		||||
  output in some cases: an example is repr(2e16+8), which gives
 | 
			
		||||
  '2.000000000000001e+16'; without this change it would have produced
 | 
			
		||||
  '20000000000000010.0' instead.
 | 
			
		||||
 | 
			
		||||
- Similarly, the str function switches to exponential notation at
 | 
			
		||||
- The str function switches to exponential notation at
 | 
			
		||||
  1e11, not 1e12.  This avoids printing 13 significant digits in
 | 
			
		||||
  situations where only 12 of them are correct.  Example problem
 | 
			
		||||
  value: str(1e11 + 0.5).  (This minor issue has existed in 2.x for a
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +37,9 @@ Core and Builtins
 | 
			
		|||
  finite float x, repr(x) now outputs a string based on the shortest
 | 
			
		||||
  sequence of decimal digits that rounds to x.  Previous behaviour was
 | 
			
		||||
  to output 17 significant digits and then strip trailing zeros.
 | 
			
		||||
  Another minor difference is that the new repr switches to
 | 
			
		||||
  exponential notation at 1e16 instead of the previous 1e17; this
 | 
			
		||||
  avoids misleading output in some cases.
 | 
			
		||||
 | 
			
		||||
  There's a new sys attribute sys.float_repr_style, which takes
 | 
			
		||||
  the value 'short' to indicate that we're using short float repr,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -485,6 +485,50 @@ PyOS_ascii_formatd(char       *buffer,
 | 
			
		|||
 | 
			
		||||
/* The fallback code to use if _Py_dg_dtoa is not available. */
 | 
			
		||||
 | 
			
		||||
/* Remove trailing zeros after the decimal point from a numeric string; also
 | 
			
		||||
   remove the decimal point if all digits following it are zero.  The numeric
 | 
			
		||||
   string must end in '\0', and should not have any leading or trailing
 | 
			
		||||
   whitespace.  Assumes that the decimal point is '.'. */
 | 
			
		||||
Py_LOCAL_INLINE(void)
 | 
			
		||||
remove_trailing_zeros(char *buffer)
 | 
			
		||||
{
 | 
			
		||||
	char *old_fraction_end, *new_fraction_end, *end, *p;
 | 
			
		||||
 | 
			
		||||
	p = buffer;
 | 
			
		||||
	if (*p == '-' || *p == '+')
 | 
			
		||||
		/* Skip leading sign, if present */
 | 
			
		||||
		++p;
 | 
			
		||||
	while (isdigit(Py_CHARMASK(*p)))
 | 
			
		||||
		++p;
 | 
			
		||||
 | 
			
		||||
	/* if there's no decimal point there's nothing to do */
 | 
			
		||||
	if (*p++ != '.')
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* scan any digits after the point */
 | 
			
		||||
	while (isdigit(Py_CHARMASK(*p)))
 | 
			
		||||
		++p;
 | 
			
		||||
	old_fraction_end = p;
 | 
			
		||||
 | 
			
		||||
	/* scan up to ending '\0' */
 | 
			
		||||
	while (*p != '\0')
 | 
			
		||||
		p++;
 | 
			
		||||
	/* +1 to make sure that we move the null byte as well */
 | 
			
		||||
	end = p+1;
 | 
			
		||||
 | 
			
		||||
	/* scan back from fraction_end, looking for removable zeros */
 | 
			
		||||
	p = old_fraction_end;
 | 
			
		||||
	while (*(p-1) == '0')
 | 
			
		||||
		--p;
 | 
			
		||||
	/* and remove point if we've got that far */
 | 
			
		||||
	if (*(p-1) == '.')
 | 
			
		||||
		--p;
 | 
			
		||||
	new_fraction_end = p;
 | 
			
		||||
 | 
			
		||||
	memmove(new_fraction_end, old_fraction_end, end-old_fraction_end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyAPI_FUNC(char *) PyOS_double_to_string(double val,
 | 
			
		||||
                                         char format_code,
 | 
			
		||||
                                         int precision,
 | 
			
		||||
| 
						 | 
				
			
			@ -498,6 +542,7 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
 | 
			
		|||
	char *p;
 | 
			
		||||
	int t;
 | 
			
		||||
	int upper = 0;
 | 
			
		||||
	int strip_trailing_zeros = 0;
 | 
			
		||||
 | 
			
		||||
	/* Validate format_code, and map upper and lower case */
 | 
			
		||||
	switch (format_code) {
 | 
			
		||||
| 
						 | 
				
			
			@ -532,8 +577,17 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
 | 
			
		|||
			PyErr_BadInternalCall();
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		precision = 12;
 | 
			
		||||
		format_code = 'g';
 | 
			
		||||
		/* switch to exponential notation at 1e11, or 1e12 if we're
 | 
			
		||||
		   not adding a .0 */
 | 
			
		||||
		if (fabs(val) >= (flags & Py_DTSF_ADD_DOT_0 ? 1e11 : 1e12)) {
 | 
			
		||||
			precision = 11;
 | 
			
		||||
			format_code = 'e';
 | 
			
		||||
			strip_trailing_zeros = 1;
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			precision = 12;
 | 
			
		||||
			format_code = 'g';
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		PyErr_BadInternalCall();
 | 
			
		||||
| 
						 | 
				
			
			@ -554,11 +608,14 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
 | 
			
		|||
		t = Py_DTST_FINITE;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		if (flags & Py_DTSF_ADD_DOT_0)
 | 
			
		||||
		if ((flags & Py_DTSF_ADD_DOT_0) && (format_code != 'e'))
 | 
			
		||||
			format_code = 'Z';
 | 
			
		||||
 | 
			
		||||
		PyOS_snprintf(format, 32, "%%%s.%i%c", (flags & Py_DTSF_ALT ? "#" : ""), precision, format_code);
 | 
			
		||||
		PyOS_ascii_formatd(buf, sizeof(buf), format, val);
 | 
			
		||||
		/* remove trailing zeros if necessary */
 | 
			
		||||
		if (strip_trailing_zeros)
 | 
			
		||||
			remove_trailing_zeros(buf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	len = strlen(buf);
 | 
			
		||||
| 
						 | 
				
			
			@ -671,7 +728,7 @@ format_float_short(double d, char format_code,
 | 
			
		|||
	assert(digits_end != NULL && digits_end >= digits);
 | 
			
		||||
	digits_len = digits_end - digits;
 | 
			
		||||
 | 
			
		||||
	if (digits_len && !isdigit(digits[0])) {
 | 
			
		||||
	if (digits_len && !isdigit(Py_CHARMASK(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. */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue