bug in gmp_fprintf?

Torbjörn Granlund tg at gmplib.org
Mon Nov 23 22:35:28 UTC 2015


paul zimmermann <Paul.Zimmermann at inria.fr> writes:

         Hi,
  
  on https://gmplib.org/list-archives/gmp-discuss/2005-August/001787.html I read:
  
  > "Support at the mpn and mpz levels for operands of up to 2^56 bits
  > (on 64-bit machines). Current limits are: on 32-bit machines, 2^31 bits;
  > on 64-bit machines, 2^37 bits".
  
  However with the program below I get on a 64-bit machine:
  
  zimmerma at tomate:/tmp$ ./a.out 8589934589
  GMP version 6.1.0
  P has 8589934589 bits
  P has 1 bits
  
  If I replace gmp_fprintf (fp, "%Zx\n", P) by mpz_out_str (fp, 16, P) I get:
  
  zimmerma at tomate:/tmp$ ./a.out 8589934589
  GMP version 6.1.0
  P has 8589934589 bits
  P has 8589934581 bits
  
  
Thanks, we'll look into this bug (or these bugs).  Presumably some
32-bit counts have sneaked in somewhere in the conversion code.

In reproducing the error, I ran into embarrassingly slow code in
scanf/doscan.c; we read a byte at a time through a callback (see GET())
and test the base argument in a tight, serially dependent loop.

Clearly, the code should be replaced to something along these lines:

      blk_size = 4;       // initial input block size
      cnt = 10;           // count to next block size increase
      while (...)
        {
          nread = fread (bp, 1, blk_size, fp);
          if (nread != blk_size)
            {
              TODO: This does not mean we're hit the end of things; if we're
              reading from a pipe we can get partial block reads.  We should
              probably simply loop around read, or fall back to getc.  Note
              that the code below expects an even # of bytes, padding/sentinel
              would be needed for odd sizes.
            }

          c1 = map[bp[0]];
          for (i = blk_size - 2; i != 0; i -= 2)
            {
              c2 = map[bp[1]];
              if (c1 == 0)
                goto end_of_string;
              c1 = map[bp[2]];
              if (c2 == 0)
                goto end_of_string;
              bp += 2;
            }
          c2 = map[bp[1]];
          if (c1 == 0)
            goto end_of_string;
          if (c2 == 0)
            goto end_of_string;

          if (--cnt == 0)
            {
              blk_size = blk_size * 2;
              cnt = 10;
              if (blk_size > 1024)
                cnt = MAX_ULONG;   // stop increasing size
              buf2 = realloc(buf, ...);
              bp += buf2 - buf;
              buf = buf2;
            }
        }
    end_of_string:
      while (...)
        ungetc(bp[...], fp);
   mp?_set_str (...);


Such code would be around 2 orders of magnitude faster than the present
code.

One could handle any base, and even floats and retionals by manipulating
map[] on-the-fly.  (E.g., allow digits and '/' in the initial map[],
then patch map[] to make it allow just digits.


-- 
Torbjörn
Please encrypt, key id 0xC8601622


More information about the gmp-bugs mailing list