java小数后三位小数_关于java:舍入到三位小数的最快方法是什么?

本文探讨了在密集数学代码中遇到的浮点精度问题,如何通过优化`roundTwoDecimals`函数和使用`BigDecimal`避免精度损失。作者分享了将数值转换为整数并乘以1000以减少四舍五入误差的高效方法,以及使用`Math.floor`和`casting`技巧。通过实例展示了性能提升后的代码执行速度对比。
摘要由CSDN通过智能技术生成

So社区是对的,在你问性能问题之前分析你的代码似乎比我随机猜测的方法更有意义:—)我分析了我的代码(非常密集的数学),没有意识到超过70%的代码显然在某种程度上,我不认为是减速的原因,小数四舍五入。

static double roundTwoDecimals(double d) {

DecimalFormat twoDForm = new DecimalFormat("#.###");

return Double.valueOf(twoDForm.format(d));

}

我的问题是我得到的十进制数通常是0.01,.02等,但有时我得到的是0.07000000001(我真的只关心0.07,但浮点精度会导致我的其他公式失败),我只是希望前3位小数避免这个问题。

那么,有没有更好/更快的方法来做到这一点?

使用BigDecimal。

它是因为速度慢,还是因为在循环等中频繁调用这个方法而在那里花费了这么多时间?

你知道吗,因为像0.070这样的东西不能表示为P/2?它实际上不能精确地表示为一个double?

@托马斯说得对。我经常在循环中调用它,但是我在同一循环中做了很多其他的事情。出于某种原因,这花费的时间最多(不是我怀疑的10+数学公式或变量查找等)。我仍在学习如何描述,所以我可能做错了,因为它似乎是程序中最无关紧要的部分。

@我明白这一点,但我在找百分位,最深的是三个点(7.1%=0.071)。精确的数字会使我的程序有点混乱,所以我试着把它四舍五入到3。

您可以通过立即初始化decimalformat而不是在每次调用时创建新的decimalformat来稍微提高性能。但是,在创建和解析字符串时额外的开销仍然会损害您的性能。把所有的东西乘以1000,使用整数或长整数,正如亚当·利斯所建议的那样,将会给你最好的表现。

@学习Java:我希望你不要用浮动来代表金钱?我相信你知道,除了粗略估计的计算之外,永远不应该这样做。

@9000不,只是数量的百分之一(与钱无关)。

@福阿德,我很肯定BigDecimal是最慢的方法。:

将(正数)取整的标准方法如下:

double rounded = floor(1000 * doubleVal + 0.5) / 1000;

例1:floor(1000 * .1234 + 0.5) / 1000=floor(123.9)/1000=0.123。例2:floor(1000 * .5678 + 0.5) / 1000=floor(568.3)/1000=0.568。

但是正如@nuakh所评论的,在某种程度上,你总是会被舍入错误所困扰。如果您只需要3位小数,最好是将其转换为千分之一(即,将所有内容乘以1000),并使用整型数据类型(int、long等)。

在这种情况下,您将跳过最后的1000除法,并使用积分123和568进行计算。如果您希望结果以百分比的形式显示,您将除以10显示:

123→12.3%568→56.8%

对不起,我不能完全理解……我有什么办法可以避免它吗?基本上我要做的是百分之几,所以三个是我最需要的空间。07%或07.1%获得.07000000001不足以改变我的计划。

正确的。所以,不用说0.071,乘以1000,用71来计算。然后,完成后,将最终结果除以10(即,除以1000,然后乘以100%),得到7.1%,然后显示。

好吧,我来试一试。事实上,我不想添加与之相关的新细节(但似乎与我无关),但我实际上是在从for循环(1到100,在另一种情况下是1到1000)中生成百分比,然后将结果乘以,得到一个百分比公式。我将不得不重构相当多的公式,但我认为这是可能的。我会把结果告诉你的。

顺便说一下,你的结果和我目前的结果不一样。例如:34*.01=0.34。亚当公式:(1000*(c*.01)+0.5)/1000 0.3405 35*.01=0.35。亚当公式:(1000*(c*.01)+0.5)/1000 0.350500000000003 36*.01=0.36。亚当的公式:(1000*(C*.01)+0.5)/1000 0.3605对不起,格式设置已关闭。

更新并提供了示例。

伙计,你个摇滚!!你的公式将我的代码执行时间从15-20秒减少到6秒!我将把代码转换为不使用小数,而是使用int,让您知道结果。非常感谢!我觉得这个剖析器会教我解决问题的新方法!

乐于助人。通过学习分析代码,你为自己做了很大的贡献。真正的调试器比printf更好。记住,优化调试代码比调试优化代码更容易。-)

很甜。基本上,我只需编写普通的蹩脚代码并保存结果(我知道这些结果是正确的),然后根据这些结果继续运行我的代码,以测试我是否破坏了任何东西。profiler有很多帮助,但我还没有使用调试器(需要了解)。如果你发现你的代码速度慢,分析器说需要花费时间的代码是Java库,比如java. U.L.ActuditList.Idter(只是一个例子),那么我还有一个问题(忽略,如果超出范围)。这是否意味着我应该找到头韵库,或者我还能做些什么?

在这种情况下,问问你自己是否有一个不同的实现(比如改变单位,使用整数而不是浮点)可能更适合你的目的。但有时,如果它的好处是显著的(可维护性、可移植性等),那么它就值得在性能上受到影响。有时,如果不让自己感觉需要洗澡,就没有好的方法来加快速度。

太棒了……再次感谢亚当。我问了一个问题,但从你那里得到了很多知识。非常感谢。

使用石膏比使用地板或圆形更快。我怀疑热点编译器对强制转换的优化程度更高。

public class Main {

public static final int ITERS = 1000 * 1000;

public static void main(String... args) {

for (int i = 0; i < 3; i++) {

perfRoundTo3();

perfCastRoundTo3();

}

}

private static double perfRoundTo3() {

double sum = 0.0;

long start = 0;

for (int i = -20000; i < ITERS; i++) {

if (i == 0) start = System.nanoTime();

sum += roundTo3(i * 1e-4);

}

long time = System.nanoTime() - start;

System.out.printf("Took %,d ns per round%n", time / ITERS);

return sum;

}

private static double perfCastRoundTo3() {

double sum = 0.0;

long start = 0;

for (int i = -20000; i < ITERS; i++) {

if (i == 0) start = System.nanoTime();

sum += castRoundTo3(i * 1e-4);

}

long time = System.nanoTime() - start;

System.out.printf("Took %,d ns per cast round%n", time / ITERS);

return sum;

}

public static double roundTo3(double d) {

return Math.round(d * 1000 + 0.5) / 1000.0;

}

public static double castRoundTo3(double d) {

return (long) (d * 1000 + 0.5) / 1000.0;

}

}

印刷品

Took 22 ns per round

Took 9 ns per cast round

Took 23 ns per round

Took 6 ns per cast round

Took 20 ns per round

Took 6 ns per cast round

注意:对于Java 7层(x+1)和圆(x),不做与这个问题完全相同的事情。为什么Math.Round(0.49999999999994)返回1

这将正确地四舍五入到表示错误内。这意味着,虽然结果不精确,但十进制(例如0.001)并没有精确表示,但当您使用toString()时,它将对此进行更正。只有当您转换为bigdecimal或执行算术运算时,才会看到此表示错误。

很好,但您没有尝试使用JMH运行这些测试吗?

"YeTaAutoMeCudio"在编写之前使用JMH,但如果Java 8和JMH产生不同的结果,则可能值得一看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值