如何快速检查是否适合浮动?(JAVA)

Tra*_*ers 7 java floating-point

是否有一些算术或按位运算可以检查双精度是否适合浮点而不会损失精度.

它不仅要检查双范围是否在浮点范围内,还要检查没有尾数位丢失.

再见

PS:这回答了C#中途的问题: 如何在不转换为无限的情况下检查双精度是否适合浮点 但是我需要一个适用于Java的解决方案.

sla*_*dan 6

直接的解决方案可能如下所示:

public class Scribble {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {

            double d = 1d / ((double)i);
            float f = (float) d;

            boolean lossless = d == f;

            System.out.println(d + " can be converted " + (lossless ? "lossless" : "only with loss"));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它输出:

1.0 can be converted lossless
0.5 can be converted lossless
0.3333333333333333 can be converted only with loss
0.25 can be converted lossless
0.2 can be converted only with loss
0.16666666666666666 can be converted only with loss
0.14285714285714285 can be converted only with loss
0.125 can be converted lossless
0.1111111111111111 can be converted only with loss
0.1 can be converted only with loss
Run Code Online (Sandbox Code Playgroud)

编辑:速度比较显示,方法2似乎是最快的:

 method1  |  method2  |  method3 
237094654 | 209365345 | 468025911
214129288 | 209917275 | 448695709
232093486 | 197637245 | 448153336
249210162 | 200163771 | 460200921
240685446 | 200638561 | 447061763
332890287 | 337870633 | 450452194
247054322 | 199045232 | 449442540
235533069 | 200767924 | 452743201
256274670 | 199153775 | 453373979
298277375 | 198659529 | 456672251
229360115 | 205883096 | 454198291
252680123 | 224850463 | 452860277
246047739 | 200070587 | 458091501
304270790 | 204517093 | 463688631
235058620 | 204675812 | 448639390
260565871 | 205834286 | 458372075
256008432 | 242574024 | 498943242
311210028 | 208080237 | 478777466
242014926 | 208995343 | 457901380
239893559 | 205111348 | 451616471
Run Code Online (Sandbox Code Playgroud)

码:

public class Scribble {

    static int size = 1024*1024*100;
    static boolean[] results = new boolean[size];
    static double[] values = new double[size];

    public static void main(String[] args) {

        // generate values
        for (int i = 0; i < size; i++)
            values[i] = 1d / ((double)i);

        long start;
        long duration;

        System.out.println(" method1  |  method2  |  method3 ");
        for (int i = 0; i < 20; i++) {
            start = System.nanoTime();
            method1(size);
            duration = System.nanoTime() - start;
            System.out.printf("%9d", duration);

            start = System.nanoTime();
            method2(size);
            duration = System.nanoTime() - start;
            System.out.printf(" | %9d", duration);

            start = System.nanoTime();
            method3(size);
            duration = System.nanoTime() - start;
            System.out.printf(" | %9d\n", duration);
        }
    }

    private static void method1(int size) {
        boolean[] results = new boolean[size];
        for (int i = 0; i < size; i++) {
            double d = values[i];
            float f = (float) d;

            boolean lossless = d == f;
            results[i] = lossless;
        }
    }

    private static void method2(int size) {
        for (int i = 0; i < size; i++) {
            double d = values[i];
            results[i] = d == (double)(float)d;
        }
    }

    private static void method3(int size) {
        for (int i = 0; i < size; i++) {
            double d = values[i];
            results[i] = Double.compare(d, (float) d) == 0;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


gex*_*ide 5

这个怎么样:

double d = ...;
if ((double)(float)d == d) {
   System.out.println(d + " fits into float!");
}
Run Code Online (Sandbox Code Playgroud)

这个想法非常简单:我们首先进行转换float然后返回double并检查结果是否仍然相同.如果d不适合浮动,则在(float)d施法时会丢失一些精度,因此结果会有所不同.

严格地说,double由于比较运算符会隐式地执行此操作,因此返回到没有必要,所以(float)d == d也没关系.

如果你担心这个的性能,因为许多浮点运算比类似的int操作慢得多:这在这里几乎不是问题.float和double之间的转换在现代CPU中非常有效.它甚至可以被矢量化!指令集中有cvtpd2pscvtps2pd指令SSE2执行从double到float的转换,反之亦然(一次转换4个值).这些指令在支持它们的所有Intel CPU上具有4个周期的延迟.4次转换的4个周期非常快.

  • 这实际上是无用的,因为它对大多数小数都失败了。例如 `0.1 == (float) 0.1` 返回 `false`... (2认同)