Search code examples
cgmpfloating-point-precision

Calculate machine precision on gmp arbitrary precision


I'm trying to obtain the machine precision on gmp variables.

To that end, I adapted the code from wikipedia, to compute the precision of a gmp with a fixed precision:

int main( int argc, char **argv )
{
    long int precision = 100;

mpf_set_default_prec(precision); // in principle redundant, but who cares

    mpf_t machEps, one, temp; // "one" is the "1" in gmp. tmp is to comparison.
    mpf_init2(machEps, precision); 
    mpf_set_ui(machEps, 1); // eps = 1
    mpf_init_set(one,machEps); // ensure "one" has the same precision as machEps
    mpf_init_set(temp,machEps); // ensure "temp" has the same precision as machEps

    do {
        mpf_div_ui(machEps,machEps,2); // eps = eps/2
        mpf_div_ui(temp,machEps,2);    // temp = eps/2
        mpf_add(temp,temp,one);        // temp += 1
    }
    while ( mpf_cmp(temp,one)); // temp == 1
    /// print the result...
    char *t = new char[400];
    mp_exp_t expprt;
    mpf_get_str(NULL, &expprt, 10, 10, machEps);

    sprintf(t, "%se%ld", mpf_get_str(NULL, &expprt, 10, mpf_get_default_prec(), machEps), expprt);

    printf( "Calculated Machine epsilon: %s\n", t);
    return 0;
}

However, the result is not consistent with the wikipedia's formula, neither changes with the precision I set. What am I missing? I've also tried with double and float (c standard), and the result is correct...


Solution

  • I get results that are consistent with wikipedia's formula, and the values depend on the precision.

    However, the value - and the effective precision - only change when crossing a limb-boundary(1). For me, that means multiples of 64, so for

    (k-1)*64 < precision <= k*64
    

    the calculated machine epsilon is

    0.5^(k*64)
    

    Some results:

    $ ./a.out 192
    Calculated Machine epsilon: 15930919111324522770288803977677118055911045551926187860739e-57
    $ ./a.out 193
    Calculated Machine epsilon: 8636168555094444625386351862800399571116000364436281385023703470168591803162427e-77
    

    For comparison:

    Prelude> 0.5^192
    1.5930919111324523e-58
    Prelude> 0.5^256
    8.636168555094445e-78
    

    The output of the GMP programme is in the form mantissa,'e',exponent where the value is

    0.mantissa * 10^exponent
    

    (1) GMP represents the floating point numbers as a pair of exponent (for base 2) and mantissa (and sign). The mantissa is maintained as an array of unsigned integers, the limbs. For me, the limbs are 64 bits, on 32 bit systems usually 32 bits (iirc). So when the desired precision is between (k-1)*LIMB_BITS (exclusive) and k*LIMB_BITS (inclusive), the array for the mantissa contains k limbs, and all of them are used, thus the effective precision is k*LIMB_BITS bits. Therefore the epsilon only changes when the number of limbs changes.