@@ -235,95 +235,97 @@ void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number)
235235
236236/*-----------------------------------------------*/
237237#ifndef UNITY_EXCLUDE_FLOAT_PRINT
238- static void UnityPrintDecimalAndNumberWithLeadingZeros (UNITY_INT32 fraction_part , UNITY_INT32 divisor )
239- {
240- UNITY_OUTPUT_CHAR ('.' );
241- while (divisor > 0 )
242- {
243- UNITY_OUTPUT_CHAR ('0' + fraction_part / divisor );
244- fraction_part %= divisor ;
245- divisor /= 10 ;
246- if (fraction_part == 0 ) break ; /* Truncate trailing 0's */
247- }
248- }
249- #ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO
250- /* If rounds up && remainder 0.5 && result odd && below cutoff for double precision issues */
251- #define ROUND_TIES_TO_EVEN (orig , num_int , num ) \
252- if (num_int > (num) && (num) - (num_int-1) <= 0.5 && (num_int & 1) == 1 && orig < 1e22) \
253- num_int -= 1 /* => a tie to round down to even */
254- #else
255- #define ROUND_TIES_TO_EVEN (orig , num_int , num ) /* Remove macro */
256- #endif
257-
258- /* Printing floating point numbers is hard. Some goals of this implementation: works for embedded
259- * systems, floats or doubles, and has a reasonable format. The key paper in this area,
260- * 'How to Print Floating-Point Numbers Accurately' by Steele & White, shows an approximation by
261- * scaling called Dragon 2. This code uses a similar idea. The other core algorithm uses casts and
262- * floating subtraction to give exact remainders after the decimal, to be scaled into an integer.
263- * Extra trailing 0's are excluded. The output defaults to rounding to nearest, ties to even. You
264- * can enable rounding ties away from zero. Note: UNITY_DOUBLE param can typedef to float or double
265-
266- * The old version required compiling in snprintf. For reference, with a similar format as now:
267- * char buf[19];
268- * if (number > 4294967296.0 || -number > 4294967296.0) snprintf(buf, sizeof buf, "%.8e", number);
269- * else snprintf(buf, sizeof buf, "%.6f", number);
270- * UnityPrint(buf);
271- */
238+ /* This function prints a floating-point value in a format similar to
239+ * printf("%.6g"). It can work with either single- or double-precision,
240+ * but for simplicity, it prints only 6 significant digits in either case.
241+ * Printing more than 6 digits accurately is hard (at least in the single-
242+ * precision case) and isn't attempted here. */
272243void UnityPrintFloat (const UNITY_DOUBLE input_number )
273244{
274- UNITY_DOUBLE number ;
245+ UNITY_DOUBLE number = input_number ;
275246
276- if (input_number < 0 )
247+ /* print minus sign (including for negative zero) */
248+ if (number < 0.0f || (number == 0.0f && 1.0f / number < 0.0f ))
277249 {
278250 UNITY_OUTPUT_CHAR ('-' );
279- number = - input_number ;
280- } else
281- {
282- number = input_number ;
251+ number = - number ;
283252 }
284253
285- if (isnan (number )) UnityPrint (UnityStrNaN );
286- else if (isinf (number )) UnityPrintLen (UnityStrInf , 3 );
287- else if (number <= 0.0000005 && number > 0 ) UnityPrint ("0.000000..." ); /* Small number */
288- else if (number < 4294967295.9999995 ) /* Rounded result fits in 32 bits, "%.6f" format */
254+ /* handle zero, NaN, and +/- infinity */
255+ if (number == 0.0f ) UnityPrint ("0" );
256+ else if (isnan (number )) UnityPrint ("nan" );
257+ else if (isinf (number )) UnityPrint ("inf" );
258+ else
289259 {
290- const UNITY_INT32 divisor = 1000000 /10 ;
291- UNITY_UINT32 integer_part = (UNITY_UINT32 )number ;
292- UNITY_INT32 fraction_part = (UNITY_INT32 )((number - (UNITY_DOUBLE )integer_part )* 1000000.0 + 0.5 );
293- /* Double precision calculation gives best performance for six rounded decimal places */
294- ROUND_TIES_TO_EVEN (number , fraction_part , (number - (UNITY_DOUBLE )integer_part )* 1000000.0 );
260+ int exponent = 0 ;
261+ int decimals , digits ;
262+ UNITY_INT32 n ;
263+ char buf [16 ];
264+
265+ /* scale up or down by powers of 10 */
266+ while (number < 100000.0f / 1e6f ) { number *= 1e6f ; exponent -= 6 ; }
267+ while (number < 100000.0f ) { number *= 10.0f ; exponent -- ; }
268+ while (number > 1000000.0f * 1e6f ) { number /= 1e6f ; exponent += 6 ; }
269+ while (number > 1000000.0f ) { number /= 10.0f ; exponent ++ ; }
295270
296- if (fraction_part == 1000000 ) /* Carry across the decimal point */
271+ /* round to nearest integer */
272+ n = ((UNITY_INT32 )(number + number ) + 1 ) / 2 ;
273+ if (n > 999999 )
297274 {
298- fraction_part = 0 ;
299- integer_part += 1 ;
275+ n = 100000 ;
276+ exponent ++ ;
300277 }
301278
302- UnityPrintNumberUnsigned (integer_part );
303- UnityPrintDecimalAndNumberWithLeadingZeros (fraction_part , divisor );
304- }
305- else /* Number is larger, use exponential format of 9 digits, "%.8e" */
306- {
307- const UNITY_INT32 divisor = 1000000000 /10 ;
308- UNITY_INT32 integer_part ;
309- UNITY_DOUBLE_TYPE divide = 10.0 ;
310- int exponent = 9 ;
279+ /* determine where to place decimal point */
280+ decimals = (exponent <= 0 && exponent >= -9 ) ? - exponent : 5 ;
281+ exponent += decimals ;
311282
312- while (number / divide >= 1000000000.0 - 0.5 )
283+ /* truncate trailing zeroes after decimal point */
284+ while (decimals > 0 && n % 10 == 0 )
313285 {
314- divide *= 10 ;
315- exponent ++ ;
286+ n /= 10 ;
287+ decimals -- ;
288+ }
289+
290+ /* build up buffer in reverse order */
291+ digits = 0 ;
292+ while (n != 0 || digits < decimals + 1 )
293+ {
294+ buf [digits ++ ] = (char )('0' + n % 10 );
295+ n /= 10 ;
296+ }
297+ while (digits > 0 )
298+ {
299+ if (digits == decimals ) UNITY_OUTPUT_CHAR ('.' );
300+ UNITY_OUTPUT_CHAR (buf [-- digits ]);
301+ }
302+
303+ /* print exponent if needed */
304+ if (exponent != 0 )
305+ {
306+ UNITY_OUTPUT_CHAR ('e' );
307+
308+ if (exponent < 0 )
309+ {
310+ UNITY_OUTPUT_CHAR ('-' );
311+ exponent = - exponent ;
312+ }
313+ else
314+ {
315+ UNITY_OUTPUT_CHAR ('+' );
316+ }
317+
318+ digits = 0 ;
319+ while (exponent != 0 || digits < 2 )
320+ {
321+ buf [digits ++ ] = (char )('0' + exponent % 10 );
322+ exponent /= 10 ;
323+ }
324+ while (digits > 0 )
325+ {
326+ UNITY_OUTPUT_CHAR (buf [-- digits ]);
327+ }
316328 }
317- integer_part = (UNITY_INT32 )(number / divide + 0.5 );
318- /* Double precision calculation required for float, to produce 9 rounded digits */
319- ROUND_TIES_TO_EVEN (number , integer_part , number / divide );
320-
321- UNITY_OUTPUT_CHAR ('0' + integer_part / divisor );
322- UnityPrintDecimalAndNumberWithLeadingZeros (integer_part % divisor , divisor / 10 );
323- UNITY_OUTPUT_CHAR ('e' );
324- UNITY_OUTPUT_CHAR ('+' );
325- if (exponent < 10 ) UNITY_OUTPUT_CHAR ('0' );
326- UnityPrintNumber (exponent );
327329 }
328330}
329331#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */
0 commit comments