Skip to content

Commit 7a2402c

Browse files
committed
test: Add decimal input/output to long_double tests
Now that tinystdio has decimal support for long double, test it. Signed-off-by: Keith Packard <[email protected]>
1 parent 7889013 commit 7a2402c

File tree

1 file changed

+158
-39
lines changed

1 file changed

+158
-39
lines changed

test/long_double.c

Lines changed: 158 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -211,18 +211,21 @@ naive_strtold(const char *buf)
211211
{
212212
long double v = 0.0L;
213213
long exp = 0;
214-
long double frac_mul = 1.0L / 16.0L;
214+
long double frac_mul;
215215
long exp_sign = 1;
216+
long double base = 10.0L;
216217
char c;
217218
enum {
218219
LDOUBLE_INT,
219220
LDOUBLE_FRAC,
220221
LDOUBLE_EXP,
221222
} state = LDOUBLE_INT;
222223

223-
if (strncmp(buf, "0x", 2) != 0)
224-
return -(long double)INFINITY;
225-
buf += 2;
224+
if (strncmp(buf, "0x", 2) == 0) {
225+
base = 16.0L;
226+
buf += 2;
227+
}
228+
frac_mul = 1.0L / base;
226229
while ((c = *buf++)) {
227230
int digit;
228231
switch (c) {
@@ -233,9 +236,11 @@ naive_strtold(const char *buf)
233236
}
234237
return -(long double)INFINITY;
235238
case 'p':
236-
if (state == LDOUBLE_INT || state == LDOUBLE_FRAC) {
237-
state = LDOUBLE_EXP;
238-
continue;
239+
if (base == 16.0L) {
240+
if (state == LDOUBLE_INT || state == LDOUBLE_FRAC) {
241+
state = LDOUBLE_EXP;
242+
continue;
243+
}
239244
}
240245
return -(long double)INFINITY;
241246
case '-':
@@ -254,15 +259,41 @@ naive_strtold(const char *buf)
254259
case '5': case '6': case '7': case '8': case '9':
255260
digit = c - '0';
256261
break;
262+
case 'E':
263+
if (base == 10.0L) {
264+
if (state == LDOUBLE_INT || state == LDOUBLE_FRAC) {
265+
state = LDOUBLE_EXP;
266+
continue;
267+
}
268+
return -(long double)INFINITY;
269+
}
270+
/* FALLTHROUGH */
271+
if (base == 10.0L) {
272+
if (state == LDOUBLE_INT || state == LDOUBLE_FRAC) {
273+
state = LDOUBLE_EXP;
274+
continue;
275+
}
276+
return -(long double)INFINITY;
277+
}
278+
/* FALLTHROUGH */
257279
case 'A': case 'B': case 'C':
258-
case 'D': case 'E': case 'F':
280+
case 'D': case 'F':
259281
if (state == LDOUBLE_INT || state == LDOUBLE_FRAC) {
260282
digit = c - 'A' + 10;
261283
break;
262284
}
263285
return -(long double)INFINITY;
286+
case 'e':
287+
if (base == 10.0L) {
288+
if (state == LDOUBLE_INT || state == LDOUBLE_FRAC) {
289+
state = LDOUBLE_EXP;
290+
continue;
291+
}
292+
return -(long double)INFINITY;
293+
}
294+
/* FALLTHROUGH */
264295
case 'a': case 'b': case 'c':
265-
case 'd': case 'e': case 'f':
296+
case 'd': case 'f':
266297
if (state == LDOUBLE_INT || state == LDOUBLE_FRAC) {
267298
digit = c - 'a' + 10;
268299
break;
@@ -273,62 +304,150 @@ naive_strtold(const char *buf)
273304
}
274305
switch (state) {
275306
case LDOUBLE_INT:
276-
v = v * 16.0L + digit;
307+
v = v * base + digit;
277308
break;
278309
case LDOUBLE_FRAC:
279310
v = v + digit * frac_mul;
280-
frac_mul *= 1.0L / 16.0L;
311+
frac_mul *= 1.0L / base;
281312
break;
282313
case LDOUBLE_EXP:
283314
exp = exp * 10 + digit;
284315
break;
285316
}
286317
}
287-
return ldexpl(v, exp * exp_sign);
318+
if (base == 10.0L) {
319+
long etop = exp / 2;
320+
long ebot = exp - etop;
321+
long double epow_top = powl(10.0L, etop * exp_sign);
322+
long double epow_bot = powl(10.0L, ebot * exp_sign);
323+
long double vpow = v * epow_top;
324+
long double r = vpow * epow_bot;
325+
return r;
326+
} else
327+
return ldexpl(v, exp * exp_sign);
328+
}
329+
330+
static const char *formats[] = { "%La", "%.30Le", };
331+
332+
#define NFMTS (sizeof (formats)/sizeof(formats[0]))
333+
334+
static bool
335+
close(long double have, long double want, long double max_error)
336+
{
337+
if (have == want)
338+
return true;
339+
340+
if (max_error == 0.0L)
341+
return false;
342+
343+
if (want == 0.0L)
344+
return fabsl(have) <= max_error;
345+
return fabsl((have - want) / want) <= max_error;
288346
}
289347

348+
static const int test_exp[] = {
349+
__LDBL_MIN_EXP__ - __LDBL_MANT_DIG__ - 1,
350+
__LDBL_MIN_EXP__ - __LDBL_MANT_DIG__,
351+
__LDBL_MIN_EXP__ - __LDBL_MANT_DIG__ + 1,
352+
__LDBL_MIN_EXP__ - __LDBL_MANT_DIG__ + 2,
353+
__LDBL_MIN_EXP__ - __LDBL_MANT_DIG__ + 3,
354+
__LDBL_MIN_EXP__ - 3,
355+
__LDBL_MIN_EXP__ - 2,
356+
__LDBL_MIN_EXP__ - 1,
357+
__LDBL_MIN_EXP__,
358+
__LDBL_MIN_EXP__ + 1,
359+
__LDBL_MIN_EXP__ + 2,
360+
__LDBL_MIN_EXP__ + 3,
361+
-3,
362+
-2,
363+
-1,
364+
0,
365+
1,
366+
2,
367+
3,
368+
__LDBL_MAX_EXP__ - 3,
369+
__LDBL_MAX_EXP__ - 2,
370+
__LDBL_MAX_EXP__ - 1,
371+
__LDBL_MAX_EXP__,
372+
__LDBL_MAX_EXP__ + 1,
373+
};
374+
375+
#define NEXPS (sizeof (test_exp)/ sizeof(test_exp[0]))
376+
377+
/*
378+
* For 64-bit values, we may have exact conversions. Otherwise, allow
379+
* some error
380+
*/
381+
#ifdef _IO_FLOAT_EXACT
382+
# if __SIZEOF_LONG_DOUBLE__ == 8
383+
# define MAX_DECIMAL_ERROR 0
384+
# else
385+
# define MAX_DECIMAL_ERROR 1e-10L
386+
# endif
387+
#else
388+
# if __SIZEOF_LONG_DOUBLE__ == 8
389+
# define MAX_DECIMAL_ERROR 1e-5L
390+
# else
391+
# define MAX_DECIMAL_ERROR 1e-10L
392+
# endif
393+
#endif
394+
290395
static int
291396
test_io(void)
292397
{
293-
int e;
398+
unsigned e;
294399
int result = 0;
295400
char buf[80];
296-
unsigned i;
401+
unsigned i, j;
402+
long double max_error, max_error_naive;
297403
char *end;
298404

299-
for (e = __LDBL_MIN_EXP__ - __LDBL_MANT_DIG__; e <= __LDBL_MAX_EXP__; e++)
405+
for (e = 0; e < NEXPS; e++)
300406
{
301-
long double v, r;
302407
for (i = 0; i < NVALS; i++) {
303-
v = ldexpl(vals[i], e);
304-
sprintf(buf, "%La", v);
305-
if (isinf(v)) {
306-
if (strcmp(buf, "inf") != 0) {
307-
printf("test_io i %d val %La exp %d: is %s should be inf\n", i, vals[i], e, buf);
308-
result++;
408+
409+
long double v, r;
410+
v = ldexpl(vals[i], test_exp[e]);
411+
412+
for (j = 0; j < NFMTS; j++) {
413+
414+
if (j == 0) {
415+
max_error = 0;
416+
max_error_naive = 0;
417+
} else {
418+
max_error = MAX_DECIMAL_ERROR;
419+
max_error_naive = 1e-6L;
309420
}
310-
} else if (isnan(v)) {
311-
if (strcmp(buf, "nan") != 0) {
312-
printf("test_io is %s should be nan\n", buf);
421+
422+
sprintf(buf, formats[j], v);
423+
if (isinf(v)) {
424+
if (strcmp(buf, "inf") != 0) {
425+
printf("test_io i %d val %La exp %d: is %s should be inf\n", i, vals[i], test_exp[e], buf);
426+
result++;
427+
}
428+
} else if (isnan(v)) {
429+
if (strcmp(buf, "nan") != 0) {
430+
printf("test_io is %s should be nan\n", buf);
431+
result++;
432+
}
433+
} else {
434+
r = naive_strtold(buf);
435+
if (!close(r, v, max_error_naive)) {
436+
printf("test_io naive i %d val %La exp %d: \"%s\", is %La should be %La\n", i, vals[i], test_exp[e], buf, r, v);
437+
result++;
438+
}
439+
}
440+
sscanf(buf, "%Lf", &r);
441+
if (!close(r, v, max_error) && !(isnan(v) && isnan(r))) {
442+
printf("test_io scanf i %d val %La exp %d: \"%s\", is %La should be %La\n", i, vals[i], test_exp[e], buf, r, v);
313443
result++;
314444
}
315-
} else {
316-
r = naive_strtold(buf);
317-
if (v != r) {
318-
printf("test_io naive i %d val %La exp %d: \"%s\", is %La should be %La\n", i, vals[i], e, buf, r, v);
445+
r = strtold(buf, &end);
446+
if ((!close(r, v, max_error) && !(isnan(v) && isnan(r)))|| end != buf + strlen(buf)) {
447+
printf("test_io strtold i %d val %La exp %d: \"%s\", is %La should be %La\n", i, vals[i], test_exp[e], buf, r, v);
319448
result++;
320449
}
321450
}
322-
sscanf(buf, "%Lf", &r);
323-
if (v != r && !(isnan(v) && isnan(r))) {
324-
printf("test_io scanf i %d val %La exp %d: \"%s\", is %La should be %La\n", i, vals[i], e, buf, r, v);
325-
result++;
326-
}
327-
r = strtold(buf, &end);
328-
if ((v != r && !(isnan(v) && isnan(r)))|| end != buf + strlen(buf)) {
329-
printf("test_io strtold i %d val %La exp %d: \"%s\", is %La should be %La\n", i, vals[i], e, buf, r, v);
330-
result++;
331-
}
332451
}
333452
}
334453
return result;

0 commit comments

Comments
 (0)