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