使用IEEE-754标准,当0 < ABS(const) < 1时,(x / const) * const是否保证对于不同的X值返回不同的结果?

12

假设我执行以下操作:

(X / const) * const

使用双精度参数,其定义由IEEE 754-2008确定,先进行除法再进行乘法。

const的范围为0 < ABS(const) < 1

假设操作成功(没有溢出发生),对于此操作的不同参数X是否保证返回不同的结果?

换句话说,是否存在X1X20 < ABS(const) < 1,使得X1 <> X2,但是(X1 / const) * const = (X2 / const) * const


1
“X的不同参数”是指双精度变量中的不同参数还是指对这些双精度变量的赋值中的不同参数?例如,“double X1 = 9007199254740992ULL;”和“double X2 = 9007199254740993ULL;”是不同的赋值,但在变量中具有相同的值(9007199254740992)。 - Rick Regan
他的意思是独特的值(数学定义),而不是独特的赋值。 - Broam
1
对我来说,“数学定义”就是我展示的那个例子。从数学上讲,它们是不同的;但在转换成双精度浮点数时,它们就不再是不同的了。 - Rick Regan
1
@Rick:通过独特的参数,我指的是64位双精度值,在比较时应该返回false。 +0-0不是不同的。在你的情况下,X1X2将按位相同,因此不是不同的。 - Quassnoi
2个回答

7

是的。

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); } }

输出:

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

能够看到x1、x2和c的真实值而不是打印出来的四舍五入的值会很好。如果你在Linux下使用printf("%1.53f",...)运行它,我们就可以看到真实的值了。 - Rick Regan
四舍五入后的值应足以重建准确的值。实际上,这是在Linux上运行的Java 1.6,甚至对于%1.53f,也只有更多的零可见。 - starblue
真的 - 我只是想看看它们。有趣 - 我很惊讶您没有得到所有数字(虽然我从未在Linux上尝试过Java,但我希望它像我尝试过的其他一切一样(请参阅我的文章http://www.exploringbinary.com/print-precision-of-dyadic-fractions-varies-by-language/了解详情)。 - Rick Regan

2
我想在starblue的回答中补充一些内容——它太长了,无法放入评论中。当我能够看到double的完整二进制值时,我发现更容易理解正在发生的事情,希望您也是这样。我将starblue的示例放入了一个C程序中,并将输出转换为二进制(使用我的转换程序,网址为http://www.exploringbinary.com/converting-floating-point-numbers-to-binary-strings-in-c/)。下面是输出结果以及计算结果:
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

顺便说一下,“* const”部分是不必要的:仅通过const除法就可以表明X1 / const == X2 / const。

当您将双精度值与真正的任意精度值进行比较时,您可以真正看到发生了什么。例如,以第一个例子为例:

x1/c = x2/c (double) = 100100.01010001101110101101000101101100011111011010101
x1/c (true) = 100100.01010001101110101101000101101100011111011010100 1011...
x2/c (true) = 100100.01010001101110101101000101101100011111011010101 0111...

我在重要位53和54之间放了一个空格,这是在双倍中进行舍入的位置。x1/c四舍五入,而x2/c向下取整,变成相同的值。


@Rick:关于“不必要的表达式”:由于const小于1,可能会出现商不同但乘积相同的情况。当我提出这个问题时,我并不确定商可能会有所不同(并且对它们可以有所不同感到惊讶)。 - Quassnoi
我只是指出在这十个例子中,仅仅是除法引起了“问题”(我在我的机器上尝试了它们所有)。 - Rick Regan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接