in reply to Re^2: Behaviour of int() unexpected
in thread Behaviour of int() unexpected

Oh, and how do you imagine 1/3 being stored in your model? There's no pair of integers m and e where m * 10^e gives 1/3. Your model doesn't work for periodic numbers.

You could approximate it. You could use m = 3333333333 and e = -10, but it's not quite exact.

And guess what. That's exactly the problem faced here.

The number is stored as two integers. But it's not a power of 10; it's a power of 2.

So, in this case, you need a pair of integers m and e where m * 2^e = 895/100. There is no such pair of integers because 895/100 is periodic in binary.

So the computer used m = 0x11E66666666666 and e = -49, but it's not quite exact.

$ perl -Mv5.14 -e'say 8.95 == 0x11E66666666666 * 2**(-49) ? 1 : 0' 1 $ perl -Mv5.14 -e'say sprintf "%.751g", 0x11E66666666666 * 2**(-49)' 8.949999999999999289457264239899814128875732421875 $ perl -Mv5.14 -e'say sprintf "%.751g", 8.95' 8.949999999999999289457264239899814128875732421875

If you want to be picky, an IEEE double is actually stored as follows to get a couple of extra bits:

( -1 )^s * ( 1 + ( m * 2^( -52 ) ) * 2^( e - 1023 ) )

Replies are listed 'Best First'.
Re^4: Behaviour of int() unexpected
by harangzsolt33 (Deacon) on Mar 11, 2025 at 02:41 UTC
    Okay, so if 1/3 gives us a problem, why didn't they introduce another variable such as r which would count the repeating digits at the end?

    So, a number like 23.676767676767 might be represented as m = 2367 e = -2 r = 2 where r tells us to repeat the last two digits to infinity. Or 1/3 would be stored as m = 3 e = -1 r = 1

    In a 64-bit variable, it would be 4 bits for r, 10 bits for e, and 48 bits for m. Then there would be one bit for mantissa's sign and another bit for exponent's sign. Total 64 bits.

    Mantissa would go from -281474976710656 to +281474976710656
    Exp would go from -1024 to +1024
    Rep would go from 0 to 15

      IIRC do some languages offer a data type for fractions.

      So 4/6 is stored as a tuple (2,3)

      I think Raku is one of them.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      see Wikisyntax for the Monastery

        Yes, Raku is one such language.
        Perl also provides this through the Math::BigRat module, and also the bigrat pragma:
        > >perl -Mbigrat -le "$x = 4; print $x/6;" 2/3
        Math::GMPq is another module that provides for rational arithmetic.

        Cheers,
        Rob
        > data type for fractions.

        I should have mentioned that any periodic number can be represented loss free as fraction, and vice versa.

        (I already covered this in school in 7th or 8th grade but curriculums may vary.)

        So, within the boundaries of integer precision you can have loss free calculations.

        But with some overhead for normalization. (I suppose they can't be delayed ...🤔 ;)

        This could be sufficient for financial applications, though I'd rather prefer to calculate with the smallest unit in integer. ( Like cent, or deci-cent or whatever depending on legal specifications)

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

      It would be very expensive to perform math operations on a number stored that way, it at all possible.

      Assuming it's possible, even straight up comparisons would be super expensive. If you multiply m = 3 e = -1 r = 1 by three, you get m = 9 e = -1 r = 1. But that's equal to m = 1 e = 0 r = 0. Should every math operation perform this expensive normalization? Should comparison? Neither are appealing.

      And to what end? To avoid rounding? You'll need to do that anyway when r != 0, and you usually want to do that regardless. To make int work better? int will still give the wrong result in the example above (m = 9 e = -1 r = 1) without normalization.