我试图通过以下方法分离5.6(例如):
private static double[] method(double d)
{
int integerPart = 0;
double fractionPart = 0.0;
integerPart = (int) d;
fractionPart = d - integerPart;
return new double[]{integerPart, fractionPart};
}
但我得到的是:
[0] = 5.0
[1] = 0.5999999999999996
如果不将数字转换为字符串,您对此有何建议?
答案 0 :(得分:12)
使用BigDecimal
进行相同的计算。 (使用双精度因其表现而存在精度问题)。
new BigDecimal(String.valueOf(yourDouble))
构造它(这仍然是通过字符串,但部分不是通过字符串操作分开的)bd.subtract(new BigDecimal(bd.intValue())
确定分数答案 1 :(得分:5)
以下是另一种基于BigDecimal
的解决方案(不会通过String
)。
private static double[] method(double d) {
BigDecimal bd = new BigDecimal(d);
return new double[] { bd.intValue(),
bd.remainder(BigDecimal.ONE).doubleValue() };
}
正如您将注意到的那样,您仍然不会仅获得0.6
作为小数部分的输出。 (你甚至不能将0.6
存储在double
中!)这是因为数学实数5.6实际上并不是由双精确表示为5.6而是5.599999。
您也可以
private static double[] method(double d) {
BigDecimal bd = BigDecimal.valueOf(d);
return new double[] { bd.intValue(),
bd.remainder(BigDecimal.ONE).doubleValue() };
}
实际上会产生[5.0, 0.6]
。
BigDecimal.valueOf
在大多数JDK中(内部)通过调用Double.toString
来实现。但至少与字符串相关的东西不会使你的代码混乱: - )
评论中的好后续问题:
如果它表示为5.599999999 ...,那么为什么
Double.toString(5.6)
完全给出"5.6"
Double.toString
方法实际上非常复杂。来自documentation of Double.toString
:
[...]
m或a的小数部分必须打印多少位数?必须至少有一个数字来表示小数部分,并且除此之外必须有多个,但只有多少,更多的数字才能唯一地将参数值与double类型的相邻值区分开来。也就是说,假设x是由此方法为有限非零参数d生成的十进制表示所表示的精确数学值。那么d必须是最接近x的double值;或者如果两个double值同样接近x,那么d必须是其中之一,d的有效位的最低有效位必须为0.
[...]
获取字符"5.6"
的代码归结为FloatingDecimal.getChars
:
private int getChars(char[] result) {
assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
int i = 0;
if (isNegative) { result[0] = '-'; i = 1; }
if (isExceptional) {
System.arraycopy(digits, 0, result, i, nDigits);
i += nDigits;
} else {
if (decExponent > 0 && decExponent < 8) {
// print digits.digits.
int charLength = Math.min(nDigits, decExponent);
System.arraycopy(digits, 0, result, i, charLength);
i += charLength;
if (charLength < decExponent) {
charLength = decExponent-charLength;
System.arraycopy(zero, 0, result, i, charLength);
i += charLength;
result[i++] = '.';
result[i++] = '0';
} else {
result[i++] = '.';
if (charLength < nDigits) {
int t = nDigits - charLength;
System.arraycopy(digits, charLength, result, i, t);
i += t;
} else {
result[i++] = '0';
}
}
} else if (decExponent <=0 && decExponent > -3) {
result[i++] = '0';
result[i++] = '.';
if (decExponent != 0) {
System.arraycopy(zero, 0, result, i, -decExponent);
i -= decExponent;
}
System.arraycopy(digits, 0, result, i, nDigits);
i += nDigits;
} else {
result[i++] = digits[0];
result[i++] = '.';
if (nDigits > 1) {
System.arraycopy(digits, 1, result, i, nDigits-1);
i += nDigits-1;
} else {
result[i++] = '0';
}
result[i++] = 'E';
int e;
if (decExponent <= 0) {
result[i++] = '-';
e = -decExponent+1;
} else {
e = decExponent-1;
}
// decExponent has 1, 2, or 3, digits
if (e <= 9) {
result[i++] = (char)(e+'0');
} else if (e <= 99) {
result[i++] = (char)(e/10 +'0');
result[i++] = (char)(e%10 + '0');
} else {
result[i++] = (char)(e/100+'0');
e %= 100;
result[i++] = (char)(e/10+'0');
result[i++] = (char)(e%10 + '0');
}
}
}
return i;
}
答案 2 :(得分:1)
要了解发生了什么,请查看数字的二进制表示:
double d = 5.6;
System.err.printf("%016x%n", Double.doubleToLongBits(d));
double[] parts = method(d);
System.err.printf("%016x %016x%n",
Double.doubleToLongBits(parts[0]),
Double.doubleToLongBits(parts[1]));
输出:
4016666666666666
4014000000000000 3fe3333333333330
5.6是1.4 * 2 2 ,但0.6是1.2 * 2 -1 。因为它具有较低的指数,所以归一化导致尾数向左移位三位。重复出现的术语(..66666..
)最初是7/5分数的近似值这一事实已被遗忘,缺失的位用零替换。
将原始double
值作为方法的输入,无法避免这种情况。要保留准确的值,您需要使用完全代表所需值的格式,例如来自Apache commons-math的Fraction
。 (对于d=5.6
的具体示例,BigDecimal
也可以准确地表示它,但还有其他数字无法准确表示,例如4/3)
答案 3 :(得分:0)
穷人解决方案(使用String)
static double[] sp(double d) {
String str = String.format(Locale.US, "%f", d);
int i = str.indexOf('.');
return new double[] {
Double.parseDouble(str.substring(0, i)),
Double.parseDouble(str.substring(i))
};
}
(Locale所以我们真的得到小数点)
答案 4 :(得分:-1)
String doubleAsString = Double.toString(123.456);
String beforeDecimal = doubleAsString.substring(0,doubleAsString.indexOf(&#34;。&#34;)); // 123
String afterDecimal = doubleAsString.substring(doubleAsString.indexOf(&#34;。&#34;)+ 1); // 456