12

Assume I do this operation:

(X / const) * const

with double-precision arguments as defined by IEEE 754-2008, division first, then multiplication.

const is in the range 0 < ABS(const) < 1.

Assuming that the operation succeeds (no overflows occur), are distinct arguments of X to this operation guaranteed to return distinct results?

In other words, are there any X1, X2 and 0 < ABS(const) < 1 so that X1 <> X2, but (X1 / const) * const = (X2 / const) * const?

starblue
  • 55,348
  • 14
  • 97
  • 151
Quassnoi
  • 413,100
  • 91
  • 616
  • 614
  • 1
    By "distinct arguments of X" do you mean distinct in the double variables or distinct in the assignment to those double variables? For example, "double X1 = 9007199254740992ULL;" and "double X2 = 9007199254740993ULL;" are distinct assignments but have the same value in the variable (9007199254740992). – Rick Regan Feb 09 '10 at 18:30
  • He means distinct values (the mathematical definition) not distinct assignments. – Broam Feb 09 '10 at 18:48
  • 1
    To me the "mathematical definition" is exactly the example I showed. Mathematically, they're distinct; rounded into a double, they're not. – Rick Regan Feb 09 '10 at 19:01
  • 1
    @Rick: by distinct arguments I mean `64-bit` double-precision values that should return false on comparison. `+0` and `-0` are not distinct. In your case, `X1` and `X2` will be bitwise identical, hence not distinct. – Quassnoi Feb 09 '10 at 21:46

2 Answers2

7

Yes.

public class TestDoubleDivision
{
    public static void main(String[] args)
    {
        final Random random = new Random();
        int i = 0;
        while (i < 10)
        {
            final double c = random.nextDouble();
            final double x1 = 10.0 * random.nextDouble();
            final double x2 = nextDouble(x1);

            if (x1 / c * c == x2 / c * c)
            {
                System.out.printf("x1 = %.20f, x2 = %.20f, c = %.20f\n", x1, x2, c);
                i++;
            }
        }
    }


    private static double nextDouble(double d1)
    {
        return Double.longBitsToDouble(Double.doubleToLongBits(d1) + 1);
    }
}

prints

x1 = 5.77383813703796800000, x2 = 5.77383813703796900000, c = 0.15897456707659440000
x1 = 2.97635611350670850000, x2 = 2.97635611350670900000, c = 0.15347615678619309000
x1 = 7.98634439050267450000, x2 = 7.98634439050267500000, c = 0.83202322046715640000
x1 = 0.11618686267768408000, x2 = 0.11618686267768409000, c = 0.09302449134082225000
x1 = 0.98646731978098480000, x2 = 0.98646731978098490000, c = 0.40549842805620606000
x1 = 3.95828649870362700000, x2 = 3.95828649870362750000, c = 0.75526917984495820000
x1 = 1.65404856207794440000, x2 = 1.65404856207794460000, c = 0.14500102367827516000
x1 = 5.72713430182017500000, x2 = 5.72713430182017550000, c = 0.68241935505532810000
x1 = 3.71143195248990980000, x2 = 3.71143195248991000000, c = 0.21294683305890750000
x1 = 5.66441726170857800000, x2 = 5.66441726170857900000, c = 0.69355199625947250000
starblue
  • 55,348
  • 14
  • 97
  • 151
  • It would be nice to see the true values of x1, x2, and c, instead of the rounded values that are printed. If you were to run this under Linux using printf ("%1.53f",...), we could see the true values. – Rick Regan Feb 11 '10 at 14:24
  • The rounded values should be enough to reconstruct the exact value. Actually this is Java 1.6 running on Linux, and there is nothing but more zeros to be seen, even for %1.53f. – starblue Feb 11 '10 at 19:13
  • True -- I just wanted to see them. Funny -- I'm surprised that you don't get all the digits (though I never tried Java on Linux, I expected it to be like everything else I tried on Linux (see my article http://www.exploringbinary.com/print-precision-of-dyadic-fractions-varies-by-language/ for details). – Rick Regan Feb 11 '10 at 20:06
2

(I just wanted to add something to starblue's answer -- it's too long to fit into a comment.)

I find it easier to see what's going on -- and I'm hoping you will too -- when I can see the full binary value of a double. I put starblue's examples in a C program and converted the output to binary (using my conversion program at http://www.exploringbinary.com/converting-floating-point-numbers-to-binary-strings-in-c/ ). Here's the output, plus the result of the calculation:

x1 = 101.1100011000011010010000011001001011111001110000111
x2 = 101.11000110000110100100000110010010111110011100001111
c  = 0.00101000101100101000111010100110011111010101111100001
r  = 100100.01010001101110101101000101101100011111011010101

x1 = 10.111110011111001001111001011010001100001011001111011
x2 = 10.1111100111110010011110010110100011000010110011111
c  = 0.0010011101001010001101101010001000011100110010101011
r  = 10011.011001001001100010101001001110011100011111011111

x1 = 111.1111110010000001000100001110001111001101010100101
x2 = 111.11111100100000010001000011100011110011010101001011
c  = 0.11010100111111110111100101001001011010110100010111011
r  = 1001.100110010100010010100101110100000100000110000011

x1 = 0.0001110110111110011011000001011101101100111011010101
x2 = 0.00011101101111100110110000010111011011001110110101010001
c  = 0.0001011111010000011100111111110000001001001011101001
r  = 1.00111111101111011111001110101010100101010101010101

x1 = 0.1111110010001001000111110100110100001000001101111111
x2 = 0.11111100100010010001111101001101000010000011011111111
c  = 0.01100111110011101011111010110111000101001011000000111
r  = 10.011011101100011101000000101000110110101011010011111

x1 = 11.1111010101010010010000111001010000100001011000111
x2 = 11.111101010101001001000011100101000010000101100011101
c  = 0.110000010101100101010010001010110001110001011111111
r  = 101.00111101101010110100110000011111101001010010101111

x1 = 1.1010011101101111101110100000000000011110110111110111
x2 = 1.1010011101101111101110100000000000011110110111111
c  = 0.00100101000111101100100101111110100101011010111111001
r  = 1011.011010000011101100001011000110000010011111110001

x1 = 101.10111010001001010111100100111110000111100001000011
x2 = 101.101110100010010101111001001111100001111000010001
c  = 0.101011101011001100001000111011000001111010111011011
r  = 1000.0110010001110100001001010000000101111000011111011

x1 = 11.101101100010000001100111100010010100011000001001111
x2 = 11.10110110001000000110011110001001010001100000101
c  = 0.0011011010000011101011110000001111000110010101111111
r  = 10001.01101101110011010100011111101110101011001010001

x1 = 101.10101010000101110011111111101001111011111010101111
x2 = 101.1010101000010111001111111110100111101111101011
c  = 0.1011000110001100100111111010011000000010100011
r  = 1000.0010101011010001010101111000111101110100001000001

(BTW, the "* const" part of the expression is unnecessary: the division by const alone shows that X1 / const == X2 / const.)

You can really see what's going on when you compare the double values with the true, arbitrary precision values. Take the first example, for instance:

x1/c = x2/c (double) = 100100.01010001101110101101000101101100011111011010101

x1/c (true)          = 100100.01010001101110101101000101101100011111011010100 1011...

x2/c (true)          = 100100.01010001101110101101000101101100011111011010101 0111...

I put a space between significant bits 53 and 54, where the rounding occurs in a double. x1/c rounds up, and x2/c rounds down (truncates), becoming the same value.

Rick Regan
  • 3,407
  • 22
  • 28
  • @Rick: regarding "unnesessary expression": since `const` is less than `1`, it may happen so that the quotients are different but the products are not. When I asked this I wasn't sure that the quotients may differ (and was surprised but the fact they can). – Quassnoi Feb 12 '10 at 08:49
  • I was just pointing out that for these ten examples, division alone caused the "problem" (I tried them all on my machine). – Rick Regan Feb 12 '10 at 13:43