As requested, and after further digging and testing, here's a patch that fix a 
few problems that I have found with long double formatting in hexadecimal. I 
have more coming for doubles in a much revised form.

It fixes:
- Wrong exponent was set for denormalized long doubles.
- It remove normalization in 2 places to match glibc's behavior. It also fix 
another problem, an infinite loop when formatting a zero with a fixed 
precision.
- Drop a whole digit when overflowing, again, to match glibc's behavior.

I have attached the patch as well as a c file for tests. For the tests, I 
compare the output from a version compiled with mingw and a version compiled 
natively with gcc, all on arch linux.
diff --git a/mingw-w64-crt/stdio/mingw_pformat.c b/mingw-w64-crt/stdio/mingw_pformat.c
index b80150af3..0c9a0d9bd 100644
--- a/mingw-w64-crt/stdio/mingw_pformat.c
+++ b/mingw-w64-crt/stdio/mingw_pformat.c
@@ -2016,15 +2016,7 @@ void __pformat_emit_xfloat( __pformat_fpreg_t value, __pformat_t *stream )
      * for exactly one digit discarded, shifting 4 bits per
      * digit, with up to 14 additional digits, to consume the
      * full availability of 15 precision digits).
-     *
-     * However, before we perform the rounding operation, we
-     * normalise the mantissa, shifting it to the left by as many
-     * bit positions may be necessary, until its highest order bit
-     * is set, thus preserving the maximum number of bits in the
-     * rounded result as possible.
-     */
-    while( value.__pformat_fpreg_mantissa < (LLONG_MAX + 1ULL) )
-      value.__pformat_fpreg_mantissa <<= 1;
+     */
 
     /* We then shift the mantissa one bit position back to the
      * right, to guard against possible overflow when the rounding
@@ -2047,11 +2039,15 @@ void __pformat_emit_xfloat( __pformat_fpreg_t value, __pformat_t *stream )
       value.__pformat_fpreg_mantissa <<= 1;
 
     else
+    {
       /* Otherwise the rounding adjustment would have overflowed,
        * so the carry has already filled the vacated bit; the effect
-       * of this is equivalent to an increment of the exponent.
+       * of this is equivalent to an increment of the exponent. We will
+       * discard a whole digit to match glibc's behavior.
        */
       value.__pformat_fpreg_exponent++;
+      value.__pformat_fpreg_mantissa >>= 3;
+    }
 
     /* We now complete the rounding to the required precision, by
      * shifting the unwanted digits out, from the right hand end of
@@ -2060,71 +2056,79 @@ void __pformat_emit_xfloat( __pformat_fpreg_t value, __pformat_t *stream )
     value.__pformat_fpreg_mantissa >>= 4 * (15 - stream->precision);
   }
 
-  /* Encode the significant digits of the mantissa in hexadecimal
-   * ASCII notation, ready for transfer to the output stream...
+  /* Don't print anything if mantissa is zero unless we have to satisfy 
+   * desired precision.
    */
-  while( value.__pformat_fpreg_mantissa )
+  if( value.__pformat_fpreg_mantissa || stream->precision > 0 )
   {
-    /* taking the rightmost digit in each pass...
+    /* Encode the significant digits of the mantissa in hexadecimal
+     * ASCII notation, ready for transfer to the output stream...
      */
-    unsigned c = value.__pformat_fpreg_mantissa & 0xF;
-    if( c == value.__pformat_fpreg_mantissa)
+    for( int i=stream->precision >= 15 || stream->precision < 0 ? 16 : stream->precision + 1; 
+         i>0; 
+         --i )
     {
-      /* inserting the radix point, when we reach the last,
-       * (i.e. the most significant digit), unless we found no
-       * less significant digits, with no mandatory radix point
-       * inclusion, and no additional required precision...
+      /* taking the rightmost digit in each pass...
        */
-      if( (p > buf)
-      ||  (stream->flags & PFORMAT_HASHED) || (stream->precision > 0)  )
+      unsigned c = value.__pformat_fpreg_mantissa & 0xF;
+      if( i == 1 )
       {
-	/*
-	 * Internally, we represent the radix point as an ASCII '.';
-	 * we will replace it with any locale specific alternative,
-	 * at the time of transfer to the ultimate destination.
-	 */
-	*p++ = '.';
+        /* inserting the radix point, when we reach the last,
+         * (i.e. the most significant digit), unless we found no
+         * less significant digits, with no mandatory radix point
+         * inclusion, and no additional required precision...
+         */
+        if( (p > buf)
+        ||  (stream->flags & PFORMAT_HASHED) || (stream->precision > 0)  )
+        {
+    /*
+     * Internally, we represent the radix point as an ASCII '.';
+     * we will replace it with any locale specific alternative,
+     * at the time of transfer to the ultimate destination.
+     */
+    *p++ = '.';
+        }
+
+        /* If the most significant hexadecimal digit of the encoded
+         * output value is greater than one, then the indicated value
+         * will appear too large, by an additional binary exponent
+         * corresponding to the number of higher order bit positions
+         * which it occupies...
+         */
+        while( value.__pformat_fpreg_mantissa > 1 )
+        {
+    /* so reduce the exponent value to compensate...
+     */
+    value.__pformat_fpreg_exponent--;
+    value.__pformat_fpreg_mantissa >>= 1;
+        }
       }
 
-      /* If the most significant hexadecimal digit of the encoded
-       * output value is greater than one, then the indicated value
-       * will appear too large, by an additional binary exponent
-       * corresponding to the number of higher order bit positions
-       * which it occupies...
-       */
-      while( value.__pformat_fpreg_mantissa > 1 )
+      else if( stream->precision > 0 )
+        /*
+        * we have not yet fulfilled the desired precision,
+        * and we have not yet found the most significant digit,
+        * so account for the current digit, within the field
+        * width required to meet the specified precision.
+        */
+        stream->precision--;
+
+      if( (c > 0) || (p > buf) || (stream->precision >= 0) )
       {
-	/* so reduce the exponent value to compensate...
-	 */
-	value.__pformat_fpreg_exponent--;
-	value.__pformat_fpreg_mantissa >>= 1;
+        /*
+         * Ignoring insignificant trailing zeros, (unless required to
+         * satisfy specified precision), store the current encoded digit
+         * into the pending output buffer, in LIFO order, and using the
+         * appropriate case for digits in the `A'..`F' range.
+         */
+        *p++ = c > 9 ? (c - 10 + 'A') | (stream->flags & PFORMAT_XCASE) : c + '0';
       }
-    }
-
-    else if( stream->precision > 0 )
-      /*
-       * we have not yet fulfilled the desired precision,
-       * and we have not yet found the most significant digit,
-       * so account for the current digit, within the field
-       * width required to meet the specified precision.
+      /* Shift out the current digit, (4-bit logical shift right),
+       * to align the next more significant digit to be extracted,
+       * and encoded in the next pass.
        */
-      stream->precision--;
-
-    if( (c > 0) || (p > buf) || (stream->precision >= 0) )
-    {
-      /*
-       * Ignoring insignificant trailing zeros, (unless required to
-       * satisfy specified precision), store the current encoded digit
-       * into the pending output buffer, in LIFO order, and using the
-       * appropriate case for digits in the `A'..`F' range.
-       */
-      *p++ = c > 9 ? (c - 10 + 'A') | (stream->flags & PFORMAT_XCASE) : c + '0';
+      value.__pformat_fpreg_mantissa >>= 4;
     }
-    /* Shift out the current digit, (4-bit logical shift right),
-     * to align the next more significant digit to be extracted,
-     * and encoded in the next pass.
-     */
-    value.__pformat_fpreg_mantissa >>= 4;
   }
 
   if( p == buf )
@@ -2301,16 +2305,9 @@ void __pformat_xldouble( long double x, __pformat_t *stream )
 	 */
 	if( z.__pformat_fpreg_mantissa != 0 )
 	{
-	  /* ...this mantissa represents a subnormal value;
-	   * adjust the exponent, while shifting the mantissa
-	   * to the left, until its leading bit is 1.
+	  /* ...this mantissa represents a subnormal value.
 	   */
-	  z.__pformat_fpreg_exponent = 1-0x3FFF;
-	  while( (z.__pformat_fpreg_mantissa & (LLONG_MAX + 1ULL)) == 0 )
-	  {
-	    z.__pformat_fpreg_mantissa <<= 1;
-	    --z.__pformat_fpreg_exponent;
-	  }
+	  z.__pformat_fpreg_exponent = -0x3FFF - 2;
 	}
       }
       else
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to