有人能向我解释为什么以下代码:
public class Test {
public static void main(String... args) {
round(6.2088, 3);
round(6.2089, 3);
}
private static void round(Double num, int numDecimal) {
System.out.println("BigDecimal: " + new BigDecimal(num).toString());
// Use Locale.ENGLISH for '.' as decimal separator
NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
nf.setGroupingUsed(false);
nf.setMaximumFractionDigits(numDecimal);
nf.setRoundingMode(RoundingMode.HALF_UP);
if(Math.abs(num) - Math.abs(num.intValue()) != 0){
nf.setMinimumFractionDigits(numDecimal);
}
System.out.println("Formatted: " + nf.format(num));
}
}
给出以下输出?
[me@localhost trunk]$ java Test
BigDecimal: 6.208800000000000096633812063373625278472900390625
Formatted: 6.209
BigDecimal: 6.208899999999999863575794734060764312744140625
Formatted: 6.208
以防您没有看到:“6.2089”四舍五入为3位给出输出“6.208”,而“6.2088”给出“6.209”作为输出。少即是多?
当使用Java 5、6或7时,结果很好,但这个Java 8给了我这个奇怪的输出。Java版本:
[me@localhost trunk]$ java -version
java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) Server VM (build 25.5-b02, mixed mode)
编辑:这是Java7的输出:
[me@localhost trunk]$ java Test
BigDecimal: 6.208800000000000096633812063373625278472900390625
Formatted: 6.209
BigDecimal: 6.208899999999999863575794734060764312744140625
Formatted: 6.209
Java7版本:
[me@localhost trunk]$ java -version
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) Server VM (build 24.51-b03, mixed mode)
通过代码追踪到DigitList.set
final void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) {
FloatingDecimal.BinaryToASCIIConverter fdConverter = FloatingDecimal.getBinaryToASCIIConverter(source);
boolean hasBeenRoundedUp = fdConverter.digitsRoundedUp();
我对这个bug有一个更简单的测试
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Locale;
public class Test {
public static void main(String... args) {
for (int i = 0; i < 100; i++)
test(i / 100.0);
}
private static void test(double num) {
NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
nf.setMaximumFractionDigits(1);
String round1 = nf.format(num);
NumberFormat nf2 = NumberFormat.getInstance(Locale.ENGLISH);
nf2.setMaximumFractionDigits(1);
nf2.setRoundingMode(RoundingMode.HALF_UP);
String round2 = nf2.format(num);
if (!round1.equals(round2))
System.out.printf("%s, formatted with HALF_UP was %s but should be %s%n", num, round2, round1);
}
}
印刷品
0.06, formatted with HALF_UP was 0 but should be 0.1
0.09, formatted with HALF_UP was 0 but should be 0.1
0.18, formatted with HALF_UP was 0.1 but should be 0.2
0.25, formatted with HALF_UP was 0.3 but should be 0.2
0.29, formatted with HALF_UP was 0.2 but should be 0.3
0.36, formatted with HALF_UP was 0.3 but should be 0.4
0.37, formatted with HALF_UP was 0.3 but should be 0.4
0.47, formatted with HALF_UP was 0.4 but should be 0.5
0.48, formatted with HALF_UP was 0.4 but should be 0.5
0.49, formatted with HALF_UP was 0.4 but should be 0.5
0.57, formatted with HALF_UP was 0.5 but should be 0.6
0.58, formatted with HALF_UP was 0.5 but should be 0.6
0.59, formatted with HALF_UP was 0.5 but should be 0.6
0.69, formatted with HALF_UP was 0.6 but should be 0.7
0.86, formatted with HALF_UP was 0.8 but should be 0.9
0.87, formatted with HALF_UP was 0.8 but should be 0.9
0.96, formatted with HALF_UP was 0.9 but should be 1
0.97, formatted with HALF_UP was 0.9 but should be 1
0.98, formatted with HALF_UP was 0.9 but should be 1
0.99, formatted with HALF_UP was 0.9 but should be 1
在不正确的情况下hasBeenRoundedUp
为true,这可以防止任何进一步的舍入。请注意,如果您放弃设置舍入,它有一个正确舍入的默认路径。
我不会使用数字格式。它的使用非常缓慢和复杂。
import java.math.BigDecimal;
public class Test {
public static void main(String... args) {
round(6.2088, 3);
round(6.2089, 3);
}
private static void round(double num, int numDecimal) {
BigDecimal bd = new BigDecimal(num);
BigDecimal bd2 = BigDecimal.valueOf(num);
System.out.println("new BigDecimal: " + bd);
System.out.println("BigDecimal.valueOf: " + bd2);
System.out.printf("%." + numDecimal + "f%n", num);
System.out.printf("%." + numDecimal + "f%n", bd);
System.out.printf("%." + numDecimal + "f%n", bd2);
System.out.printf("%f%n", round3(num));
System.out.printf("%s%n", round3(num));
System.out.printf("%f%n", bd.setScale(numDecimal, BigDecimal.ROUND_HALF_UP));
System.out.printf("%s%n", bd.setScale(numDecimal, BigDecimal.ROUND_HALF_UP));
System.out.printf("%f%n", bd2.setScale(numDecimal, BigDecimal.ROUND_HALF_UP));
System.out.printf("%s%n", bd2.setScale(numDecimal, BigDecimal.ROUND_HALF_UP));
}
private static double round3(double num) {
final double factor = 1e3;
return Math.round(num * factor) / factor;
}
}
使用Java 8打印。
new BigDecimal: 6.208800000000000096633812063373625278472900390625
BigDecimal.valueOf: 6.2088
6.209
6.209
6.209
6.209000
6.209
6.209000
6.209
6.209000
6.209
new BigDecimal: 6.208899999999999863575794734060764312744140625
BigDecimal.valueOf: 6.2089
6.209
6.209
6.209
6.209000
6.209
6.209000
6.209
6.209000
6.209
1.8.0_40-b25
于2015年3月3日发布(发行说明)感谢Holger回答中的研究,我能够开发一个运行时补丁,并且我的雇主已经根据GPLv2许可条款免费发布了它,其中包含Classpath Exception1(与OpenJDK源代码相同)。
补丁项目和源代码托管在GitHub上,包含有关此bug的更多详细信息以及可下载二进制文件的链接。该补丁不对磁盘上安装的Java文件进行任何修改,并且应该可以安全地在所有Oracle Java版本上使用
当修补程序检测到字节码签名表明存在缺陷时,它会用修改后的实现来替换开关案例:
if (digits[maximumDigits] > '5') {
return true;
} else if (digits[maximumDigits] == '5') {
return maximumDigits != (count - 1)
|| allDecimalDigits
|| !alreadyRounded;
}
// else
return false; // in original switch(), was: break;
我不是律师,但我的理解是,GPLv2 w/CPE允许以二进制形式进行商业使用,而无需将GPL应用于组合作品。
我可以把这个问题追溯到java类。文本数字列表第522行。
情况是,它认为十进制数字已四舍五入(与等效的BigDecimal表示法相比,这是正确的…)并决定不再四舍五入。问题是,这个决定只有在四舍五入产生的数字是5的情况下才有意义,而不是当它大于5的情况下才有意义。请注意下半部分的代码如何正确区分数字==“5”和数字
显然,这是一个bug,而且考虑到这样一个事实,即正确执行类似操作的代码(仅针对另一个方向)就在错误代码的正下方,这是一个奇怪的bug。
case HALF_UP:
if (digits[maximumDigits] >= '5') {
// We should not round up if the rounding digits position is
// exactly the last index and if digits were already rounded.
if ((maximumDigits == (count - 1)) &&
(alreadyRounded))
return false;
// Value was exactly at or was above tie. We must round up.
return true;
}
break;
case HALF_DOWN:
if (digits[maximumDigits] > '5') {
return true;
} else if (digits[maximumDigits] == '5' ) {
if (maximumDigits == (count - 1)) {
// The rounding position is exactly the last index.
if (allDecimalDigits || alreadyRounded)
/* FloatingDecimal rounded up (value was below tie),
* or provided the exact list of digits (value was
* an exact tie). We should not round up, following
* the HALF_DOWN rounding rule.
*/
return false;
else
// Value was above the tie, we must round up.
return true;
}
// We must round up if it gives a non null digit after '5'.
for (int i=maximumDigits+1; i<count; ++i) {
if (digits[i] != '0') {
return true;
}
}
}
break;
另一个数字没有发生这种情况的原因是6.2088
不是四舍五入的结果(再次,与BigDecimal
输出6.208800...
)。所以在这种情况下,它会四舍五入。
问题内容: 有人可以向我解释以下代码的原因: 给出以下输出? 如果您看不到它,则:“ 6.2089”舍入为3位数字将输出为“ 6.208”,而“ 6.2088”将输出为“ 6.209”。少即是多? 使用Java 5、6或7时,效果很好,但是此Java 8给了我奇怪的输出。Java版本: 编辑:这是Java 7的输出: Java 7版本: 问题答案: 我可以将此问题归类到522行。 这种情况是,它认
本节讨论了精度数学的四舍五入特性,ROUND()函数,以及插入DECIMAL列时的四舍五入特性。 ROUND()函数的行为取决于其参量是准确的还是近似的: ·对于准确值数值,ROUND()采用“半值向上舍入”规则:如果小数部分的值为.5或更大,如果是正数,向上取下一个整数,如果是负数,向下取下一个整数(换句话讲,以0为界限执行舍入)。如果小数部分的值小于.5,如果是正数,向下取下一个整数,如果是负
问题内容: 据我所知,当四舍五入为5时,四舍五入应按以下方式进行。 如果我们做所有的总结, 我很确定在.Net中也可以进行这种四舍五入。 但是在SQL Server中,总会四舍五入,这在数学上是不正确的。 这导致四舍五入后的总值相差1美分。 我已经尝试过使用十进制和货币数据类型。 难道我做错了什么?有没有解决的办法? 问题答案: 如果您确实想在SQL Server中使用银行取整…
问题内容: 我怎么总是将a舍入为一个,而不舍入它。我知道,但是我希望它总是四舍五入。因此,如果为,则四舍五入为4。 问题答案: 您可以使用方法。 请参阅JavaDoc链接:https ://docs.oracle.com/javase/10/docs/api/java/lang/Math.html#ceil(double ) 从文档: 细胞 返回大于或等于自变量且等于数学整数的最小(最接近负无穷大
有人能告诉我怎么打双人球吗 e、 g.从双倍值55.6666666666667开始-四舍五入到双倍值56.0000000000- 或者从55.333333333333开始-四舍五入到55.0000000000的两倍- 谢谢。
问题 你想对浮点数执行指定精度的舍入运算。 解决方案 对于简单的舍入运算,使用内置的 round(value, ndigits) 函数即可。比如: >>> round(1.23, 1) 1.2 >>> round(1.27, 1) 1.3 >>> round(-1.27, 1) -1.3 >>> round(1.25361,3) 1.254 >>> 当一个值刚好在两个边界的中间的时候, round