• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /* tarith.c
3  *
4  * Copyright (c) 2021 Cosmin Truta
5  * Copyright (c) 2011-2013 John Cunningham Bowler
6  *
7  * This code is released under the libpng license.
8  * For conditions of distribution and use, see the disclaimer
9  * and license in png.h
10  *
11  * Test internal arithmetic functions of libpng.
12  *
13  * This code must be linked against a math library (-lm), but does not require
14  * libpng or zlib to work.  Because it includes the complete source of 'png.c'
15  * it tests the code with whatever compiler options are used to build it.
16  * Changing these options can substantially change the errors in the
17  * calculations that the compiler chooses!
18  */
19 #define _POSIX_SOURCE 1
20 #define _ISOC99_SOURCE 1
21 
22 /* Obtain a copy of the code to be tested (plus other things), disabling
23  * stuff that is not required.
24  */
25 #include <math.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <assert.h>
30 
31 #include "../../pngpriv.h"
32 
33 #define png_error png_warning
34 
png_warning(png_const_structrp png_ptr,png_const_charp msg)35 void png_warning(png_const_structrp png_ptr, png_const_charp msg)
36 {
37    fprintf(stderr, "validation: %s\n", msg);
38 }
39 
40 #define png_fixed_error png_fixed_warning
41 
png_fixed_warning(png_const_structrp png_ptr,png_const_charp msg)42 void png_fixed_warning(png_const_structrp png_ptr, png_const_charp msg)
43 {
44    fprintf(stderr, "overflow in: %s\n", msg);
45 }
46 
47 #define png_set_error_fn(pp, ep, efp, wfp) ((void)0)
48 #define png_malloc(pp, s) malloc(s)
49 #define png_malloc_warn(pp, s) malloc(s)
50 #define png_malloc_base(pp, s) malloc(s)
51 #define png_calloc(pp, s) calloc(1, (s))
52 #define png_free(pp, s) free(s)
53 
54 #define png_safecat(b, sb, pos, str) (pos)
55 #define png_format_number(start, end, format, number) (start)
56 
57 #define crc32(crc, pp, s) (crc)
58 #define inflateReset(zs) Z_OK
59 
60 #define png_create_struct(type) (0)
61 #define png_destroy_struct(pp) ((void)0)
62 #define png_create_struct_2(type, m, mm) (0)
63 #define png_destroy_struct_2(pp, f, mm) ((void)0)
64 
65 #undef PNG_SIMPLIFIED_READ_SUPPORTED
66 #undef PNG_SIMPLIFIED_WRITE_SUPPORTED
67 #undef PNG_USER_MEM_SUPPORTED
68 
69 #include "../../png.c"
70 
71 /* Validate ASCII to fp routines. */
72 static int verbose = 0;
73 
validation_ascii_to_fp(int count,int argc,char ** argv)74 int validation_ascii_to_fp(int count, int argc, char **argv)
75 {
76    int    showall = 0;
77    double max_error=2;      /* As a percentage error-in-last-digit/.5 */
78    double max_error_abs=17; /* Used when precision is DBL_DIG */
79    double max = 0;
80    double max_abs = 0;
81    double test = 0; /* Important to test this. */
82    int    precision = 5;
83    int    nonfinite = 0;
84    int    finite = 0;
85    int    ok = 0;
86    int    failcount = 0;
87    int    minorarith = 0;
88 
89    while (--argc > 0)
90    {
91       if (strcmp(*++argv, "-a") == 0)
92          showall = 1;
93       else if (strcmp(*argv, "-e") == 0 && argc > 0)
94       {
95          --argc;
96          max_error = atof(*++argv);
97       }
98       else if (strcmp(*argv, "-E") == 0 && argc > 0)
99       {
100          --argc;
101          max_error_abs = atof(*++argv);
102       }
103       else
104       {
105          fprintf(stderr, "unknown argument %s\n", *argv);
106          return 1;
107       }
108    }
109 
110    do
111    {
112       size_t index;
113       int state, failed = 0;
114       char buffer[64];
115 
116       if (isfinite(test))
117          ++finite;
118       else
119          ++nonfinite;
120 
121       if (verbose)
122          fprintf(stderr, "%.*g %d\n", DBL_DIG, test, precision);
123 
124       /* Check for overflow in the buffer by setting a marker. */
125       memset(buffer, 71, sizeof buffer);
126 
127       png_ascii_from_fp(0, buffer, precision+10, test, precision);
128 
129       /* Allow for a three digit exponent, this stuff will fail if
130        * the exponent is bigger than this!
131        */
132       if (buffer[precision+7] != 71)
133       {
134          fprintf(stderr, "%g[%d] -> '%s'[%lu] buffer overflow\n",
135             test, precision, buffer, (unsigned long)strlen(buffer));
136          failed = 1;
137       }
138 
139       /* Following are used for the number parser below and must be
140        * initialized to zero.
141        */
142       state = 0;
143       index = 0;
144       if (!isfinite(test))
145       {
146          /* Expect 'inf' */
147          if (test >= 0 && strcmp(buffer, "inf") ||
148              test <  0 && strcmp(buffer, "-inf"))
149          {
150             fprintf(stderr, "%g[%d] -> '%s' but expected 'inf'\n",
151                test, precision, buffer);
152             failed = 1;
153          }
154       }
155       else if (!png_check_fp_number(buffer, precision+10, &state, &index) ||
156           buffer[index] != 0)
157       {
158          fprintf(stderr, "%g[%d] -> '%s' but has bad format ('%c')\n",
159             test, precision, buffer, buffer[index]);
160          failed = 1;
161       }
162       else if (PNG_FP_IS_NEGATIVE(state) && !(test < 0))
163       {
164          fprintf(stderr, "%g[%d] -> '%s' but negative value not so reported\n",
165             test, precision, buffer);
166          failed = 1;
167          assert(!PNG_FP_IS_ZERO(state));
168          assert(!PNG_FP_IS_POSITIVE(state));
169       }
170       else if (PNG_FP_IS_ZERO(state) && !(test == 0))
171       {
172          fprintf(stderr, "%g[%d] -> '%s' but zero value not so reported\n",
173             test, precision, buffer);
174          failed = 1;
175          assert(!PNG_FP_IS_NEGATIVE(state));
176          assert(!PNG_FP_IS_POSITIVE(state));
177       }
178       else if (PNG_FP_IS_POSITIVE(state) && !(test > 0))
179       {
180          fprintf(stderr, "%g[%d] -> '%s' but positive value not so reported\n",
181             test, precision, buffer);
182          failed = 1;
183          assert(!PNG_FP_IS_NEGATIVE(state));
184          assert(!PNG_FP_IS_ZERO(state));
185       }
186       else
187       {
188          /* Check the result against the original. */
189          double out = atof(buffer);
190          double change = fabs((out - test)/test);
191          double allow = .5 / pow(10,
192             (precision >= DBL_DIG) ? DBL_DIG-1 : precision-1);
193 
194          /* NOTE: if you hit this error case are you compiling with gcc
195           * and -O0?  Try -O2 - the errors can accumulate if the FP
196           * code above is not optimized and may drift outside the .5 in
197           * DBL_DIG allowed.  In any case a small number of errors may
198           * occur (very small ones - 1 or 2%) because of rounding in the
199           * calculations, either in the conversion API or in atof.
200           */
201          if (change >= allow && (isfinite(out) ||
202              fabs(test/DBL_MAX) <= 1-allow))
203          {
204             double percent = (precision >= DBL_DIG) ? max_error_abs : max_error;
205             double allowp = (change-allow)*100/allow;
206 
207             if (precision >= DBL_DIG)
208             {
209                if (max_abs < allowp) max_abs = allowp;
210             }
211 
212             else
213             {
214                if (max < allowp) max = allowp;
215             }
216 
217             if (showall || allowp >= percent)
218             {
219                fprintf(stderr,
220                   "%.*g[%d] -> '%s' -> %.*g number changed (%g > %g (%d%%))\n",
221                   DBL_DIG, test, precision, buffer, DBL_DIG, out, change, allow,
222                   (int)round(allowp));
223                failed = 1;
224             }
225             else
226                ++minorarith;
227          }
228       }
229 
230       if (failed)
231          ++failcount;
232       else
233          ++ok;
234 
235 skip:
236       /* Generate a new number and precision. */
237       precision = rand();
238       if (precision & 1) test = -test;
239       precision >>= 1;
240 
241       /* Generate random numbers. */
242       if (test == 0 || !isfinite(test))
243          test = precision+1;
244       else
245       {
246          /* Derive the exponent from the previous rand() value. */
247          int exponent = precision % (DBL_MAX_EXP - DBL_MIN_EXP) + DBL_MIN_EXP;
248          int tmp;
249          test = frexp(test * rand(), &tmp);
250          test = ldexp(test, exponent);
251          precision >>= 8; /* arbitrary */
252       }
253 
254       /* This limits the precision to 32 digits, enough for standard
255        * IEEE implementations which have at most 15 digits.
256        */
257       precision = (precision & 0x1f) + 1;
258    }
259    while (--count);
260 
261    printf("Tested %d finite values, %d non-finite, %d OK (%d failed) "
262       "%d minor arithmetic errors\n",
263       finite, nonfinite, ok, failcount, minorarith);
264    printf(" Error with >=%d digit precision %.2f%%\n", DBL_DIG, max_abs);
265    printf(" Error with < %d digit precision %.2f%%\n", DBL_DIG, max);
266 
267    return 0;
268 }
269 
270 /* Observe that valid FP numbers have the forms listed in the PNG extensions
271  * specification:
272  *
273  * [+,-]{integer,integer.fraction,.fraction}[{e,E}[+,-]integer]
274  *
275  * Test each of these in turn, including invalid cases.
276  */
277 typedef enum checkfp_state
278 {
279    start, fraction, exponent, states
280 } checkfp_state;
281 
282 /* The characters (other than digits) that characterize the states: */
283 static const char none[] = "";
284 static const char hexdigits[16] = "0123456789ABCDEF";
285 
286 static const struct
287 {
288    const char *start; /* Characters valid at the start */
289    const char *end;   /* Valid characters that end the state */
290    const char *tests; /* Characters to test after 2 digits seen */
291 }
292 state_characters[states] =
293 {
294    /* start:    */ { "+-.", ".eE", "+-.e*0369" },
295    /* fraction: */ { none, "eE",  "+-.E#0147" },
296    /* exponent: */ { "+-", none,  "+-.eE^0258" }
297 };
298 
299 typedef struct
300 {
301    char number[1024];  /* Buffer for number being tested */
302    int  limit;         /* Command line limit */
303    int  verbose;       /* Shadows global variable */
304    int  ctimes;        /* Number of numbers tested */
305    int  cmillions;     /* Count of millions of numbers */
306    int  cinvalid;      /* Invalid strings checked */
307    int  cnoaccept;     /* Characters not accepted */
308 }
309 checkfp_command;
310 
311 typedef struct
312 {
313    int           cnumber;          /* Index into number string */
314    checkfp_state check_state;      /* Current number state */
315    int           at_start;         /* At start (first character) of state */
316    int           cdigits_in_state; /* Digits seen in that state */
317    int           limit;            /* Limit on same for checking all chars */
318    int           state;            /* Current parser state */
319    int           is_negative;      /* Number is negative */
320    int           is_zero;          /* Number is (still) zero */
321    int           number_was_valid; /* Previous character validity */
322 }
323 checkfp_control;
324 
325 static int check_all_characters(checkfp_command *co, checkfp_control c);
326 
327 static int check_some_characters(checkfp_command *co, checkfp_control c,
328    const char *tests);
329 
check_one_character(checkfp_command * co,checkfp_control c,int ch)330 static int check_one_character(checkfp_command *co, checkfp_control c, int ch)
331 {
332    /* Test this character (ch) to ensure the parser does the correct thing.
333     */
334    size_t index = 0;
335    const char test = (char)ch;
336    int number_is_valid = png_check_fp_number(&test, 1, &c.state, &index);
337    int character_accepted = (index == 1);
338 
339    if (c.check_state != exponent && isdigit(ch) && ch != '0')
340       c.is_zero = 0;
341 
342    if (c.check_state == start && c.at_start && ch == '-')
343       c.is_negative = 1;
344 
345    if (isprint(ch))
346       co->number[c.cnumber++] = (char)ch;
347    else
348    {
349       co->number[c.cnumber++] = '<';
350       co->number[c.cnumber++] = hexdigits[(ch >> 4) & 0xf];
351       co->number[c.cnumber++] = hexdigits[ch & 0xf];
352       co->number[c.cnumber++] = '>';
353    }
354    co->number[c.cnumber] = 0;
355 
356    if (co->verbose > 1)
357       fprintf(stderr, "%s\n", co->number);
358 
359    if (++(co->ctimes) == 1000000)
360    {
361       if (co->verbose == 1)
362          fputc('.', stderr);
363       co->ctimes = 0;
364       ++(co->cmillions);
365    }
366 
367    if (!number_is_valid)
368       ++(co->cinvalid);
369 
370    if (!character_accepted)
371       ++(co->cnoaccept);
372 
373    /* This should never fail (it's a serious bug if it does): */
374    if (index != 0 && index != 1)
375    {
376       fprintf(stderr, "%s: read beyond end of string (%lu)\n",
377          co->number, (unsigned long)index);
378       return 0;
379    }
380 
381    /* Validate the new state, note that the PNG_FP_IS_ macros all return
382     * false unless the number is valid.
383     */
384    if (PNG_FP_IS_NEGATIVE(c.state) !=
385       (number_is_valid && !c.is_zero && c.is_negative))
386    {
387       fprintf(stderr, "%s: negative when it is not\n", co->number);
388       return 0;
389    }
390 
391    if (PNG_FP_IS_ZERO(c.state) != (number_is_valid && c.is_zero))
392    {
393       fprintf(stderr, "%s: zero when it is not\n", co->number);
394       return 0;
395    }
396 
397    if (PNG_FP_IS_POSITIVE(c.state) !=
398       (number_is_valid && !c.is_zero && !c.is_negative))
399    {
400       fprintf(stderr, "%s: positive when it is not\n", co->number);
401       return 0;
402    }
403 
404    /* Testing a digit */
405    if (isdigit(ch))
406    {
407       if (!character_accepted)
408       {
409          fprintf(stderr, "%s: digit '%c' not accepted\n", co->number, ch);
410          return 0;
411       }
412 
413       if (!number_is_valid)
414       {
415          fprintf(stderr, "%s: saw a digit (%c) but number not valid\n",
416             co->number, ch);
417          return 0;
418       }
419 
420       ++c.cdigits_in_state;
421       c.at_start = 0;
422       c.number_was_valid = 1;
423 
424       /* Continue testing characters in this state.  Either test all of
425        * them or, if we have already seen one digit in this state, just test a
426        * limited set.
427        */
428       if (c.cdigits_in_state < 1)
429          return check_all_characters(co, c);
430 
431       else
432          return check_some_characters(co, c,
433             state_characters[c.check_state].tests);
434    }
435 
436    /* A non-digit; is it allowed here? */
437    else if (((ch == '+' || ch == '-') && c.check_state != fraction &&
438                c.at_start) ||
439             (ch == '.' && c.check_state == start) ||
440             ((ch == 'e' || ch == 'E') && c.number_was_valid &&
441                c.check_state != exponent))
442    {
443       if (!character_accepted)
444       {
445          fprintf(stderr, "%s: character '%c' not accepted\n", co->number, ch);
446          return 0;
447       }
448 
449       /* The number remains valid after start of fraction but nowhere else. */
450       if (number_is_valid && (c.check_state != start || ch != '.'))
451       {
452          fprintf(stderr, "%s: saw a non-digit (%c) but number valid\n",
453             co->number, ch);
454          return 0;
455       }
456 
457       c.number_was_valid = number_is_valid;
458 
459       /* Check for a state change.  When changing to 'fraction' if the number
460        * is valid at this point set the at_start to false to allow an exponent
461        * 'e' to come next.
462        */
463       if (c.check_state == start && ch == '.')
464       {
465          c.check_state = fraction;
466          c.at_start = !number_is_valid;
467          c.cdigits_in_state = 0;
468          c.limit = co->limit;
469          return check_all_characters(co, c);
470       }
471 
472       else if (c.check_state < exponent && (ch == 'e' || ch == 'E'))
473       {
474          c.check_state = exponent;
475          c.at_start = 1;
476          c.cdigits_in_state = 0;
477          c.limit = co->limit;
478          return check_all_characters(co, c);
479       }
480 
481       /* Else it was a sign, and the state doesn't change. */
482       else
483       {
484          if (ch != '-' && ch != '+')
485          {
486             fprintf(stderr, "checkfp: internal error (1)\n");
487             return 0;
488          }
489 
490          c.at_start = 0;
491          return check_all_characters(co, c);
492       }
493    }
494 
495    /* Testing an invalid character */
496    else
497    {
498       if (character_accepted)
499       {
500          fprintf(stderr, "%s: character '%c' [0x%.2x] accepted\n", co->number,
501             ch, ch);
502          return 0;
503       }
504 
505       if (number_is_valid != c.number_was_valid)
506       {
507          fprintf(stderr,
508             "%s: character '%c' [0x%.2x] changed number validity\n",
509             co->number, ch, ch);
510          return 0;
511       }
512 
513       /* Do nothing - the parser has stuck; return success and keep going with
514        * the next character.
515        */
516    }
517 
518    /* Successful return (the caller will try the next character.) */
519    return 1;
520 }
521 
check_all_characters(checkfp_command * co,checkfp_control c)522 static int check_all_characters(checkfp_command *co, checkfp_control c)
523 {
524    int ch;
525 
526    if (c.cnumber+4 < sizeof co->number)
527    {
528       for (ch=0; ch<256; ++ch)
529       {
530          if (!check_one_character(co, c, ch))
531             return 0;
532       }
533    }
534 
535    return 1;
536 }
537 
check_some_characters(checkfp_command * co,checkfp_control c,const char * tests)538 static int check_some_characters(checkfp_command *co, checkfp_control c,
539    const char *tests)
540 {
541    int i;
542 
543    --(c.limit);
544 
545    if (c.cnumber+4 < sizeof co->number && c.limit >= 0)
546    {
547       if (c.limit > 0)
548       {
549          for (i=0; tests[i]; ++i)
550          {
551             if (!check_one_character(co, c, tests[i]))
552                   return 0;
553          }
554       }
555 
556       /* At the end check all the characters. */
557       else
558          return check_all_characters(co, c);
559    }
560 
561    return 1;
562 }
563 
validation_checkfp(int count,int argc,char ** argv)564 int validation_checkfp(int count, int argc, char **argv)
565 {
566    int result;
567    checkfp_command command;
568    checkfp_control control;
569 
570    command.number[0] = 0;
571    command.limit = 3;
572    command.verbose = verbose;
573    command.ctimes = 0;
574    command.cmillions = 0;
575    command.cinvalid = 0;
576    command.cnoaccept = 0;
577 
578    while (--argc > 0)
579    {
580       ++argv;
581       if (argc > 1 && strcmp(*argv, "-l") == 0)
582       {
583          --argc;
584          command.limit = atoi(*++argv);
585       }
586 
587       else
588       {
589          fprintf(stderr, "unknown argument %s\n", *argv);
590          return 1;
591       }
592    }
593 
594    control.cnumber = 0;
595    control.check_state = start;
596    control.at_start = 1;
597    control.cdigits_in_state = 0;
598    control.limit = command.limit;
599    control.state = 0;
600    control.is_negative = 0;
601    control.is_zero = 1;
602    control.number_was_valid = 0;
603 
604    result = check_all_characters(&command, control);
605 
606    printf("checkfp: %s: checked %d,%.3d,%.3d,%.3d strings (%d invalid)\n",
607       result ? "pass" : "FAIL", command.cmillions / 1000,
608       command.cmillions % 1000, command.ctimes / 1000, command.ctimes % 1000,
609       command.cinvalid);
610 
611    return result;
612 }
613 
validation_muldiv(int count,int argc,char ** argv)614 int validation_muldiv(int count, int argc, char **argv)
615 {
616    int tested = 0;
617    int overflow = 0;
618    int error = 0;
619    int error64 = 0;
620    int passed = 0;
621    int randbits = 0;
622    png_uint_32 randbuffer;
623    png_fixed_point a;
624    png_int_32 times, div;
625 
626    while (--argc > 0)
627    {
628       fprintf(stderr, "unknown argument %s\n", *++argv);
629       return 1;
630    }
631 
632    /* Find out about the random number generator. */
633    randbuffer = RAND_MAX;
634    while (randbuffer != 0) ++randbits, randbuffer >>= 1;
635    printf("Using random number generator that makes %d bits\n", randbits);
636    for (div=0; div<32; div += randbits)
637       randbuffer = (randbuffer << randbits) ^ rand();
638 
639    a = 0;
640    times = div = 0;
641    do
642    {
643       png_fixed_point result;
644       /* NOTE: your mileage may vary, a type is required below that can
645        * hold 64 bits or more, if floating point is used a 64-bit or
646        * better mantissa is required.
647        */
648       long long int fp, fpround;
649       unsigned long hi, lo;
650       int ok;
651 
652       /* Check the values, png_64bit_product can only handle positive
653        * numbers, so correct for that here.
654        */
655       {
656          long u1, u2;
657          int n = 0;
658          if (a < 0) u1 = -a, n = 1; else u1 = a;
659          if (times < 0) u2 = -times, n = !n; else u2 = times;
660          png_64bit_product(u1, u2, &hi, &lo);
661          if (n)
662          {
663             /* -x = ~x+1 */
664             lo = ((~lo) + 1) & 0xffffffff;
665             hi = ~hi;
666             if (lo == 0) ++hi;
667          }
668       }
669 
670       fp = a;
671       fp *= times;
672       if ((fp & 0xffffffff) != lo || ((fp >> 32) & 0xffffffff) != hi)
673       {
674          fprintf(stderr, "png_64bit_product %d * %d -> %lx|%.8lx not %llx\n",
675             a, times, hi, lo, fp);
676          ++error64;
677       }
678 
679       if (div != 0)
680       {
681          /* Round - this is C round to zero. */
682          if ((fp < 0) != (div < 0))
683            fp -= div/2;
684          else
685            fp += div/2;
686 
687          fp /= div;
688          fpround = fp;
689          /* Assume 2's complement here: */
690          ok = fpround <= PNG_UINT_31_MAX &&
691               fpround >= -1-(long long int)PNG_UINT_31_MAX;
692          if (!ok) ++overflow;
693       }
694       else
695         ok = 0, ++overflow, fpround = fp/*misleading*/;
696 
697       if (verbose)
698          fprintf(stderr, "TEST %d * %d / %d -> %lld (%s)\n",
699             a, times, div, fp, ok ? "ok" : "overflow");
700 
701       ++tested;
702       if (png_muldiv(&result, a, times, div) != ok)
703       {
704          ++error;
705          if (ok)
706              fprintf(stderr, "%d * %d / %d -> overflow (expected %lld)\n",
707                 a, times, div, fp);
708          else
709              fprintf(stderr, "%d * %d / %d -> %d (expected overflow %lld)\n",
710                 a, times, div, result, fp);
711       }
712       else if (ok && result != fpround)
713       {
714          ++error;
715          fprintf(stderr, "%d * %d / %d -> %d not %lld\n",
716             a, times, div, result, fp);
717       }
718       else
719          ++passed;
720 
721       /* Generate three new values, this uses rand() and rand() only returns
722        * up to RAND_MAX.
723        */
724       /* CRUDE */
725       a += times;
726       times += div;
727       div = randbuffer;
728       randbuffer = (randbuffer << randbits) ^ rand();
729    }
730    while (--count > 0);
731 
732    printf("%d tests including %d overflows, %d passed, %d failed "
733       "(%d 64-bit errors)\n", tested, overflow, passed, error, error64);
734    return 0;
735 }
736 
737 /* When FP is on this just becomes a speed test - compile without FP to get real
738  * validation.
739  */
740 #ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
741 #define LN2 .000010576586617430806112933839 /* log(2)/65536 */
742 #define L2INV 94548.46219969910586572651    /* 65536/log(2) */
743 
744 /* For speed testing, need the internal functions too: */
png_log8bit(unsigned x)745 static png_uint_32 png_log8bit(unsigned x)
746 {
747    if (x > 0)
748       return (png_uint_32)floor(.5-log(x/255.)*L2INV);
749 
750    return 0xffffffff;
751 }
752 
png_log16bit(png_uint_32 x)753 static png_uint_32 png_log16bit(png_uint_32 x)
754 {
755    if (x > 0)
756       return (png_uint_32)floor(.5-log(x/65535.)*L2INV);
757 
758    return 0xffffffff;
759 }
760 
png_exp(png_uint_32 x)761 static png_uint_32 png_exp(png_uint_32 x)
762 {
763    return (png_uint_32)floor(.5 + exp(x * -LN2) * 0xffffffffU);
764 }
765 
png_exp8bit(png_uint_32 log)766 static png_byte png_exp8bit(png_uint_32 log)
767 {
768    return (png_byte)floor(.5 + exp(log * -LN2) * 255);
769 }
770 
png_exp16bit(png_uint_32 log)771 static png_uint_16 png_exp16bit(png_uint_32 log)
772 {
773    return (png_uint_16)floor(.5 + exp(log * -LN2) * 65535);
774 }
775 #endif /* FLOATING_ARITHMETIC */
776 
validation_gamma(int argc,char ** argv)777 int validation_gamma(int argc, char **argv)
778 {
779    double gamma[9] = { 2.2, 1.8, 1.52, 1.45, 1., 1/1.45, 1/1.52, 1/1.8, 1/2.2 };
780    double maxerr;
781    int i, silent=0, onlygamma=0;
782 
783    /* Silence the output with -s, just test the gamma functions with -g: */
784    while (--argc > 0)
785       if (strcmp(*++argv, "-s") == 0)
786          silent = 1;
787       else if (strcmp(*argv, "-g") == 0)
788          onlygamma = 1;
789       else
790       {
791          fprintf(stderr, "unknown argument %s\n", *argv);
792          return 1;
793       }
794 
795    if (!onlygamma)
796    {
797       /* First validate the log functions: */
798       maxerr = 0;
799       for (i=0; i<256; ++i)
800       {
801          double correct = -log(i/255.)/log(2.)*65536;
802          double error = png_log8bit(i) - correct;
803 
804          if (i != 0 && fabs(error) > maxerr)
805             maxerr = fabs(error);
806 
807          if (i == 0 && png_log8bit(i) != 0xffffffff ||
808              i != 0 && png_log8bit(i) != floor(correct+.5))
809          {
810             fprintf(stderr, "8-bit log error: %d: got %u, expected %f\n",
811                i, png_log8bit(i), correct);
812          }
813       }
814 
815       if (!silent)
816          printf("maximum 8-bit log error = %f\n", maxerr);
817 
818       maxerr = 0;
819       for (i=0; i<65536; ++i)
820       {
821          double correct = -log(i/65535.)/log(2.)*65536;
822          double error = png_log16bit(i) - correct;
823 
824          if (i != 0 && fabs(error) > maxerr)
825             maxerr = fabs(error);
826 
827          if (i == 0 && png_log16bit(i) != 0xffffffff ||
828              i != 0 && png_log16bit(i) != floor(correct+.5))
829          {
830             if (error > .68) /* By experiment error is less than .68 */
831             {
832                fprintf(stderr,
833                   "16-bit log error: %d: got %u, expected %f error: %f\n",
834                   i, png_log16bit(i), correct, error);
835             }
836          }
837       }
838 
839       if (!silent)
840          printf("maximum 16-bit log error = %f\n", maxerr);
841 
842       /* Now exponentiations. */
843       maxerr = 0;
844       for (i=0; i<=0xfffff; ++i)
845       {
846          double correct = exp(-i/65536. * log(2.)) * (65536. * 65536);
847          double error = png_exp(i) - correct;
848 
849          if (fabs(error) > maxerr)
850             maxerr = fabs(error);
851          if (fabs(error) > 1883) /* By experiment. */
852          {
853             fprintf(stderr,
854                "32-bit exp error: %d: got %u, expected %f error: %f\n",
855                i, png_exp(i), correct, error);
856          }
857       }
858 
859       if (!silent)
860          printf("maximum 32-bit exp error = %f\n", maxerr);
861 
862       maxerr = 0;
863       for (i=0; i<=0xfffff; ++i)
864       {
865          double correct = exp(-i/65536. * log(2.)) * 255;
866          double error = png_exp8bit(i) - correct;
867 
868          if (fabs(error) > maxerr)
869             maxerr = fabs(error);
870          if (fabs(error) > .50002) /* By experiment */
871          {
872             fprintf(stderr,
873                "8-bit exp error: %d: got %u, expected %f error: %f\n",
874                i, png_exp8bit(i), correct, error);
875          }
876       }
877 
878       if (!silent)
879          printf("maximum 8-bit exp error = %f\n", maxerr);
880 
881       maxerr = 0;
882       for (i=0; i<=0xfffff; ++i)
883       {
884          double correct = exp(-i/65536. * log(2.)) * 65535;
885          double error = png_exp16bit(i) - correct;
886 
887          if (fabs(error) > maxerr)
888             maxerr = fabs(error);
889          if (fabs(error) > .524) /* By experiment */
890          {
891             fprintf(stderr,
892                "16-bit exp error: %d: got %u, expected %f error: %f\n",
893                i, png_exp16bit(i), correct, error);
894          }
895       }
896 
897       if (!silent)
898          printf("maximum 16-bit exp error = %f\n", maxerr);
899    } /* !onlygamma */
900 
901    /* Test the overall gamma correction. */
902    for (i=0; i<9; ++i)
903    {
904       unsigned j;
905       double g = gamma[i];
906       png_fixed_point gfp = floor(g * PNG_FP_1 + .5);
907 
908       if (!silent)
909          printf("Test gamma %f\n", g);
910 
911       maxerr = 0;
912       for (j=0; j<256; ++j)
913       {
914          double correct = pow(j/255., g) * 255;
915          png_byte out = png_gamma_8bit_correct(j, gfp);
916          double error = out - correct;
917 
918          if (fabs(error) > maxerr)
919             maxerr = fabs(error);
920          if (out != floor(correct+.5))
921          {
922             fprintf(stderr, "8bit %d ^ %f: got %d expected %f error %f\n",
923                j, g, out, correct, error);
924          }
925       }
926 
927       if (!silent)
928          printf("gamma %f: maximum 8-bit error %f\n", g, maxerr);
929 
930       maxerr = 0;
931       for (j=0; j<65536; ++j)
932       {
933          double correct = pow(j/65535., g) * 65535;
934          png_uint_16 out = png_gamma_16bit_correct(j, gfp);
935          double error = out - correct;
936 
937          if (fabs(error) > maxerr)
938             maxerr = fabs(error);
939          if (fabs(error) > 1.62)
940          {
941             fprintf(stderr, "16bit %d ^ %f: got %d expected %f error %f\n",
942                j, g, out, correct, error);
943          }
944       }
945 
946       if (!silent)
947          printf("gamma %f: maximum 16-bit error %f\n", g, maxerr);
948    }
949 
950    return 0;
951 }
952 
953 /**************************** VALIDATION TESTS ********************************/
954 /* Various validation routines are included herein, they require some
955  * definition for png_warning and png_error, seetings of VALIDATION:
956  *
957  * 1: validates the ASCII to floating point conversions
958  * 2: validates png_muldiv
959  * 3: accuracy test of fixed point gamma tables
960  */
961 
962 /* The following COUNT (10^8) takes about 1 hour on a 1GHz Pentium IV
963  * processor.
964  */
965 #define COUNT 1000000000
966 
main(int argc,char ** argv)967 int main(int argc, char **argv)
968 {
969    int count = COUNT;
970 
971    while (argc > 1)
972    {
973       if (argc > 2 && strcmp(argv[1], "-c") == 0)
974       {
975          count = atoi(argv[2]);
976          argc -= 2;
977          argv += 2;
978       }
979 
980       else if (strcmp(argv[1], "-v") == 0)
981       {
982          ++verbose;
983          --argc;
984          ++argv;
985       }
986 
987       else
988          break;
989    }
990 
991    if (count > 0 && argc > 1)
992    {
993       if (strcmp(argv[1], "ascii") == 0)
994          return validation_ascii_to_fp(count, argc-1, argv+1);
995       else if (strcmp(argv[1], "checkfp") == 0)
996          return validation_checkfp(count, argc-1, argv+1);
997       else if (strcmp(argv[1], "muldiv") == 0)
998          return validation_muldiv(count, argc-1, argv+1);
999       else if (strcmp(argv[1], "gamma") == 0)
1000          return validation_gamma(argc-1, argv+1);
1001    }
1002 
1003    /* Bad argument: */
1004    fprintf(stderr,
1005       "usage: tarith [-v] [-c count] {ascii,muldiv,gamma} [args]\n");
1006    fprintf(stderr, " arguments: ascii [-a (all results)] [-e error%%]\n");
1007    fprintf(stderr, "            checkfp [-l max-number-chars]\n");
1008    fprintf(stderr, "            muldiv\n");
1009    fprintf(stderr, "            gamma -s (silent) -g (only gamma; no log)\n");
1010    return 1;
1011 }
1012