Skip to content

Commit 0547aab

Browse files
authored
Merge pull request ThrowTheSwitch#291 from jlindgren90/master
Rewrite UnityPrintFloat to match printf("%.6g").
2 parents a868b2e + 2ae2bdb commit 0547aab

2 files changed

Lines changed: 173 additions & 237 deletions

File tree

src/unity.c

Lines changed: 76 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -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. */
272243
void 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

Comments
 (0)