in reply to Re^2: 64-bit digest algorithms
in thread 64-bit digest algorithms

Doubles have 52 bits. The 53rd bit is the implied leading "1".

Update: As pointed out by oshalla, the implied bit does count. As pointed out by BrowserUk, I forgot about the sign bit. So there's 54 bits of precision for signed integers.

Replies are listed 'Best First'.
Re^4: 64-bit digest algorithms
by BrowserUk (Patriarch) on Nov 15, 2008 at 06:39 UTC

    No! Doubles have 64-bits.

    Of which:

    • 1 bit is used for the sign;
    • 11 bits are used for the exponent;
    • 52 bits are used for the mantissa;
    • 1 extra bit is implied in the mantissa by the specification;

    With the result being that a 64-bit IEEE-754 double precision floating point value can act as a 53-bit signed integer for some purposes.

    If you're going to try to be a pedant, rather than contribute something useful--at least be right.

      can act as a 53-bit signed integer for some purposes.

      To be even more pedantic... :-)

      ...that would be a 53 bit unsigned integer, or a 54-bit sign and magnitude signed integer (using one bit for the sign); equivalent to a 54-bit 1's complement signed integer.

      Since 1's complement machines are as rare as rocking horse fumet, one might worry about the -0x80....0 minimum value. Happily that is quite comfortably accommodated in floating point form.

      The -0 value is generally treated as zero, so that shouldn't intrude.

      So, for practical purposes a 64-bit IEEE-754 double precision floating point value can act as a 54-bit signed integer (with the usual 2's complement -ve values).

      So:

      for my $n (53..55) { my $max = 2**($n-1) - 1 ; my $min = -(2**($n-1)) ; printf "%4d: -%s..%s\n", $n, show($min), show($max) ; } ; sub show { my ($x) = @_ ; my $ms = int(abs($x)/2**32) ; my $ls = abs($x) - ($ms * 2**32) ; sprintf '0x%04X_%04X_%04X_%04X', ($ms >> 16), ($ms & 0xFFFF), ($ls >> 16), ($ls & 0xFFFF) ; } ;
      gives:
          53: -0x0010_0000_0000_0000..0x000F_FFFF_FFFF_FFFF
          54: -0x0020_0000_0000_0000..0x001F_FFFF_FFFF_FFFF
          55: -0x0040_0000_0000_0000..0x0040_0000_0000_0000
      
      showing that 53-bit (2's complement) signed integer is on the small side, 55-bit is to big, but 54-bits is just right.


      For extra credit, those with 64-bit integers can consider (a) why:

      my $z = 0x0FED_CBA9_8765_4321 ; my $d = 9 ; printf " a = %20s * %d + i, for i = 0..%d\n", "$z", $d, $d ; # 12: 12345678901234567890 : +2345 print " i: a / $d : Error\n" ; foreach my $i (0..$d) { my $a = ($z * $d) + $i ; my $q = $a / $d ; printf " %2d: %20s : %+5d\n", $i, "$q", $q - ($z + int($i/$d)) ; } ;
      gives:
       a =  1147797409030816545 * 9 + i, for i = 0..9
        i:                a / 9 : Error
        0:  1147797409030816545 :    +0
        1: 1.14779740903082e+18 :  +128
        2: 1.14779740903082e+18 :  +128
        3: 1.14779740903082e+18 :  +128
        4: 1.14779740903082e+18 :  +128
        5: 1.14779740903082e+18 :  +128
        6: 1.14779740903082e+18 :  +128
        7: 1.14779740903082e+18 :  +128
        8: 1.14779740903082e+18 :  +128
        9:  1147797409030816546 :    +0
      
      and (b) whether this could be fixed, and finally (c) whether round-to-even is a Good Thing, with particular reference to this case.

      Bug #53784 refers.

        You can't subtract one from something to get the max. The only reason

        my $max = 2**($n-1) - 1 ;

        works is because the max is really

        my $max = 2**($n-1);