看起来BigDecimal.setScale截断到比例+ 1小数位,然后仅基于该小数进行舍入。 这是正常的还是有一种干净的方法将舍入模式应用于每一个小数?
输出:0.0697 (这不是他们在学校教我的舍入模式)
double d = 0.06974999999;
BigDecimal bd = BigDecimal.valueOf(d);
bd = bd.setScale(4, RoundingMode.HALF_UP);
System.out.println(bd);
输出:0.0698
(这是他们在学校教我的舍入模式)
double d = 0.0697444445;
BigDecimal bd = BigDecimal.valueOf(d);
int scale = bd.scale();
while (4 < scale) {
bd = bd.setScale(--scale, RoundingMode.HALF_UP);
}
System.out.println(bd);
EDITED 的 在读完一些答案后,我意识到我搞砸了一切。当我写下我的问题时,我有点沮丧。 所以,我要重写问题原因,即使答案对我有很大帮助,我仍然需要一些建议。
问题是:
我需要将0.06974999999舍入到0.0698,这是因为我知道那些小数实际上是0.6975(在我不能控制的地方出现舍入错误)。
所以我一直在玩一种“双舍入”,它分两步执行舍入:第一轮到更高的精度,然后舍入到所需的精度。
(这里我搞砸了,因为我认为每个小数位的循环会更安全)。
问题是我不知道在第一步中要舍入哪个更高的精度(我使用的是小数位数-1)。另外我不知道是否能为其他案件找到一些意想不到的结果
这是我放弃支持循环的第一种方式,在阅读你的答案后现在看起来好多了:
public static BigDecimal getBigDecimal(double value, int decimals) {
BigDecimal bd = BigDecimal.valueOf(value);
int scale = bd.scale();
if (scale - decimals > 1) {
bd = bd.setScale(scale - 1, RoundingMode.HALF_UP);
}
return bd.setScale(decimals, roundingMode.HALF_UP);
}
这些打印出以下结果:
0.0697444445 = 0.0697
0.0697499994 = 0.0697
0.0697499995 = 0.0698
0.0697499999 = 0.0698
0.0697444445 = 0.069744445 //四舍五入到9位小数
0.0697499999 = 0.069750000 //舍入到9位小数
0.069749 = 0.0698
现在的问题是,是否有更好的方法(可能是不同的舍入模式)?如果这可以安全地用作一般的舍入方法?
我需要对很多值进行舍入,并且必须在运行时选择此标准和标准方法,具体取决于我收到的数字类型似乎非常复杂。
再次感谢您的时间。
答案 0 :(得分:0)
如果您需要偏向舍入,则可以添加一个小因子。
e.g。最多可以小数点后6位。
double d =
double rounded = (long) (d * 1000000 + 0.5) / 1e6;
添加一个小因子,你需要决定你想要多少额外的东西。 e.g。
double d =
double rounded = (long) (d * 1000000 + 0.50000001) / 1e6;
e.g。
public static void main(String... args) throws IOException {
double d1 = 0.0697499994;
double r1 = roundTo4places(d1);
double d2 = 0.0697499995;
double r2= roundTo4places(d2);
System.out.println(d1 + " => " + r1);
System.out.println(d2 + " => " + r2);
}
public static double roundTo4places(double d) {
return (long) (d * 10000 + 0.500005) / 1e4;
}
打印
0.0697499994 => 0.0697
0.0697499995 => 0.0698
第一个是正确的。
0.44444444 ... 44445舍入为整数是0.0
只有0.500000000 ... 000或更多被舍入到1.0
没有舍入模式,将向下舍入0.4和向上0.45。
如果您考虑一下,您希望随机数被向上或向下舍入的机会相等。如果将足够多的随机数相加,则舍入所产生的错误将取消。
上半轮与
相同long n = (long) (d + 0.5);
您建议的舍入是
long n = (long) (d + 5.0/9);
Random r = new Random(0);
int count = 10000000;
// round using half up.
long total = 0, total2 = 0;
for (int i = 0; i < count; i++) {
double d = r.nextDouble();
int rounded = (int) (d + 0.5);
total += rounded;
BigDecimal bd = BigDecimal.valueOf(d);
int scale = bd.scale();
while (0 < scale) {
bd = bd.setScale(--scale, RoundingMode.HALF_UP);
}
int rounded2 = bd.intValue();
total2 += rounded2;
}
System.out.printf("The expected total of %,d rounded random values is %,d,%n\tthe actual total was %,d, using the biased rounding %,d%n",
count, count / 2, total, total2);
打印
The expected total of 10,000,000 rounded random values is 5,000,000,
the actual total was 4,999,646, using the biased rounding 5,555,106
答案 1 :(得分:0)
当你四舍五入时,你会看到你要四舍五入的最后一位数之后的值,在你的第一个例子中,你将0.06974999999四舍五入到4位小数。所以你有0.0697然后是4999999(或者基本上是697.4999999)。由于舍入模式为HALF_UP,因此0.499999小于0.5,因此向下舍入。
答案 2 :(得分:0)
如果0.06974999999和0.06975之间的差异非常重要,那么您应该更快地切换到BigDecimals。至少,如果性能如此重要,请找出使用long和int的方法。双人和花车不适合能分辨出1.0和0.999999999999999之间差异的人。当您使用它们时,信息会丢失,并且无法恢复它。
(这个信息看起来微不足道,说得客气一点,但是如果旅行1,000,000码让你在悬崖顶端,那么行驶1,000,001码会让你在过去悬崖的顶部那最后一个院子很重要。如果你以10亿美元的价格减掉1便士,当会计师跟你走后,你会遇到更大麻烦。)
答案 3 :(得分:0)
尝试使用上一个和下一个值来查看它们是否会缩小比例呢?
public static BigDecimal getBigDecimal(double value) {
BigDecimal bd = BigDecimal.valueOf(value);
BigDecimal next = BigDecimal.valueOf(Math.nextAfter(value, Double.POSITIVE_INFINITY));
if (next.scale() < bd.scale()) {
return next;
}
next = BigDecimal.valueOf(Math.nextAfter(value, Double.NEGATIVE_INFINITY));
if (next.scale() < bd.scale()) {
return next;
}
return bd;
}
然后可以将得到的BigDecimal四舍五入到所需的比例。
(我无法分辨这对大量值的性能影响)