在java中,将浮点值四舍五入到最接近的整数的最有效方法是什么?

在java中,将浮点值四舍五入到最接近的整数的最有效方法是什么?,java,optimization,rounding,floating-accuracy,Java,Optimization,Rounding,Floating Accuracy,我已经看到很多关于舍入浮点值的讨论,但是没有考虑到效率方面的可靠问答。这就是: 将浮点值四舍五入到最接近的整数的最有效(但正确)方法是什么 (int) (mFloat + 0.5); 或 或 还是别的什么 我最好使用标准java库中可用的东西,而不是必须导入的外部库。我应该选择Math.round(mFloat)因为它将舍入逻辑封装在方法中(即使它不是您的方法) 根据它,您编写的代码与执行Math.round的代码相同(除了检查边界大小写) 无论如何,更重要的是算法的时间复杂度,而不是像这样的

我已经看到很多关于舍入浮点值的讨论,但是没有考虑到效率方面的可靠问答。这就是:

将浮点值四舍五入到最接近的整数的最有效(但正确)方法是什么

(int) (mFloat + 0.5);

还是别的什么


我最好使用标准java库中可用的东西,而不是必须导入的外部库。

我应该选择
Math.round(mFloat)
因为它将舍入逻辑封装在方法中(即使它不是您的方法)

根据它,您编写的代码与执行
Math.round
的代码相同(除了检查边界大小写)

无论如何,更重要的是算法的时间复杂度,而不是像这样的小常数的时间。。。除非您正在编程的东西将被调用数百万次!:D


编辑:我不懂数学。是来自JDK吗?

根据我想你提到的问答,各种方法的相对效率取决于你使用的平台

但底线是:

  • 最新的JRE为
    Math.floor
    /
    StrictMath.floor
    ,以及
  • 除非你做了大量的四舍五入,否则你用哪种方式做可能没有任何区别
参考资料:

  • (“Java.lang.StrictMath.floor(double)和Java.lang.StrictMath.ceil(double)的纯Java实现)

您可以使用
System.currentTimeMillis()
对其进行基准测试。你会发现差别太小了

public class Main {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            measurementIteration();
        }
    }

    public static void measurementIteration() {
        long s, t1 = 0, t2 = 0;
        float mFloat = 3.3f;
        int f, n1 = 0, n2 = 0;
        for (int i = 0; i < 1E4; i++) {
            switch ((int) (Math.random() * 2)) {
            case 0:
                n1 += 1E4;
                s = System.currentTimeMillis();
                for (int k = 0; k < 1E4; k++)
                    f = (int) (mFloat + 0.5);
                t1 += System.currentTimeMillis() - s;
                break;
            case 1:
                n2 += 1E4;
                s = System.currentTimeMillis();
                for (int k = 0; k < 1E4; k++)
                    f = Math.round(mFloat);
                t2 += System.currentTimeMillis() - s;
                break;
            }
        }
        System.out.println(String.format("(int) (mFloat + 0.5): n1 = %d    -> %.3fms/1000", n1, t1 * 1000.0 / n1));
        System.out.println(String.format("Math.round(mFloat)  : n2 = %d    -> %.3fms/1000", n2, t2 * 1000.0 / n2));
    }
}
Java SE7上的输出(感谢alex的结果):


正如您所看到的,从SE6到SE7,
Math.round
的性能有了巨大的提高。我认为SE7中已经没有明显的差异,您应该选择您认为更容易阅读的内容。

简单地添加0.5将给出负数的错误结果。请参阅以获得更好的解决方案。

您是否有证据表明这是代码中的性能瓶颈?为什么您会在意?如果代码运行太慢,请对其进行评测。最有可能的是,舍入浮动不会成为瓶颈。如果您想检查
Math.round
的实现,您将从第一行看到代码,除了一个特殊情况。这就是为什么性能应该是一样的,特别是如果HotSpot在你的
通话中插入
的话。Jon和Philipp:我只是好奇而已。但我在应用程序中经常使用舍入,所以我希望使用最有效的方法。我在移动领域工作,所以尽量减少CPU的使用总是有好处的。但我几乎看不出寻找不同算法相对效率的知识是一种浪费。也许答案是这三个选项具有相同的复杂性。但也许不是。不管怎样,这都是有用的信息。我不明白为什么这么多用户经常质疑这类问题的价值。为了获得好的结果,您应该始终创建一个外部循环,调用测量函数大约10次,因此当HotSpot替换解释代码时,它实际上会生效。这段代码将解释后的性能与编译后的性能混合在一起。@MarkoTopolnik你的意思是这样的吗?是的,那就差不多了。您可以将println拉入方法中,这样就不必使用静态变量。这绝对是任何microbenchmark的推荐方法,我经常观察第一次或两次运行后的收缩时间。@MarkoTopolnik是的,你是对的,
t1
测量值(对于
(int)(mFloat+0.5)
)在前三次运行期间几乎减少了2倍。谢谢你们的建议,谢谢你们。我可能会在我的平台(Android)上做一些基准测试,看看是否得到同样的结果。顺便说一句,很抱歉混淆了,但是FloatMath是Android的一部分,而不是标准Java。这是我的疏忽。当你想进行基准测试时,你应该使用
System.nanoTime()
,而不是
System.currentTimeMillis()
。这不是绝对必要的。如果你测量的时间超过10毫秒,就没有特别需要
nanoTime
。这就是为什么我说“应该”而不是“必须”。尽管如此,在这种情况下总是使用nanoTime是一种很好的方式。前两种方法之间的差异实际上非常显著(虽然没有在Android上进行测试)…@PeterLawrey好的,那么我已经做对了,因为我从外部调用了10次1e4迭代方法。这甚至可以在JRockit上使用。是的,FloatMath来自android…很抱歉。我看得不够近,没有意识到它是从哪里来的。
FloatMath.floor(mFloat + 0.5);
public class Main {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            measurementIteration();
        }
    }

    public static void measurementIteration() {
        long s, t1 = 0, t2 = 0;
        float mFloat = 3.3f;
        int f, n1 = 0, n2 = 0;
        for (int i = 0; i < 1E4; i++) {
            switch ((int) (Math.random() * 2)) {
            case 0:
                n1 += 1E4;
                s = System.currentTimeMillis();
                for (int k = 0; k < 1E4; k++)
                    f = (int) (mFloat + 0.5);
                t1 += System.currentTimeMillis() - s;
                break;
            case 1:
                n2 += 1E4;
                s = System.currentTimeMillis();
                for (int k = 0; k < 1E4; k++)
                    f = Math.round(mFloat);
                t2 += System.currentTimeMillis() - s;
                break;
            }
        }
        System.out.println(String.format("(int) (mFloat + 0.5): n1 = %d    -> %.3fms/1000", n1, t1 * 1000.0 / n1));
        System.out.println(String.format("Math.round(mFloat)  : n2 = %d    -> %.3fms/1000", n2, t2 * 1000.0 / n2));
    }
}
(int) (mFloat + 0.5): n1 = 500410000    -> 0.003ms/1000
Math.round(mFloat)  : n2 = 499590000    -> 0.022ms/1000
(int) (mFloat + 0.5): n1 = 50120000 -> 0,002ms/1000
Math.round(mFloat) : n2 = 49880000 -> 0,002ms/1000