将双精度数分成两部分“整数和整数”的最佳方法是什么?分数”在java中
我尝试通过以下方法分离 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
您对在不将数字转换为字符串的情况下执行此操作有什么建议吗?
I have tried to separate 5.6 (for example) by the following method:
private static double[] method(double d)
{
int integerPart = 0;
double fractionPart = 0.0;
integerPart = (int) d;
fractionPart = d - integerPart;
return new double[]{integerPart, fractionPart};
}
But what I got is:
[0] = 5.0
[1] = 0.5999999999999996
Do you have any suggestion about doing this without converting the number to string?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
要了解发生了什么,请查看数字的二进制表示形式:
输出:
5.6 是 1.4 * 22,但 0.6 是 1.2 * 2-1。因为它的指数较低,所以归一化会导致尾数向左移动三位。重复项 (
..66666..
) 最初是分数 7/5 的近似值这一事实已被遗忘,并且丢失的位被替换为零。如果将原始
double
值作为方法的输入,则无法避免这种情况。要保留准确的值,您需要使用准确表示所需值的格式,例如分数
来自 Apache commons-math。 (对于这个d=5.6
的具体示例,BigDecimal
也能够准确地表示它,但还有其他数字它无法准确地表示,例如 4/3)To see what is going on, take a look at the binary representations of the numbers:
output:
5.6 is 1.4 * 22, but 0.6 is 1.2 * 2-1. Because it has a lower exponent, normalization causes the mantissa to be shifted three bits to the left. The fact that the recurring terms (
..66666..
) were originally an approximation of the fraction 7/5 has been forgotten, and the missing bits are replaced with zeros.Given the original
double
value as input to your method, there is no way to avoid this. To preserve the exact value you would need to use a format that represents the desired value exactly, e.g.Fraction
from Apache commons-math. (For this specific example withd=5.6
aBigDecimal
would also be able to represent it exactly, but there are other numbers it cannot represent exactly, e.g. 4/3)穷人解决方案(使用字符串)
(区域设置,因此我们确实得到了一个小数点)
poor-man solution (using String)
(Locale so we really get a decimal point)
字符串 doubleAsString = Double.toString(123.456);
String beforeDecimal=doubleAsString.substring(0,doubleAsString.indexOf(".")); //123
String afterDecimal=doubleAsString.substring(doubleAsString.indexOf(".")+1); //456
String doubleAsString = Double.toString(123.456);
String beforeDecimal=doubleAsString.substring(0,doubleAsString.indexOf(".")); //123
String afterDecimal=doubleAsString.substring(doubleAsString.indexOf(".")+1); //456
使用
BigDecimal
进行同样的计算。 (由于其表示形式,使用双精度数存在精度问题)。Use
BigDecimal
to do that same calculation. (using doubles has precision problems because of its representation).new BigDecimal(String.valueOf(yourDouble))
(this is still going through string, but the parts are not separated via string manipulation)bd.subtract(new BigDecimal(bd.intValue())
to determine the fraction这是另一个基于 BigDecimal 的解决方案(不通过 String)。
正如您将注意到的,您仍然不会得到
0.6
作为小数部分的输出。 (您甚至无法将0.6
存储在double
中!)这是因为数学实数 5.6 实际上并不完全由 double 表示5.6 但作为 5.599999...您也可以这样做
,实际上确实会产生
[5.0, 0.6]
。然而,
BigDecimal.valueOf
在大多数 JDK 中(内部)是通过调用Double.toString
实现的。但至少与字符串相关的东西不会扰乱你的代码:-)评论中很好的后续问题:
Double.toString
方法实际上非常复杂。来自的文档Double.toString
:获取字符的代码
"5.6"< /code> 归结为
FloatingDecimal.getChars
:Here is another solution based on
BigDecimal
(that does not go through aString
).As you'll note, you still won't get just
0.6
as output for the fractional part. (You can't even store0.6
in adouble
!) This is due to the fact that the mathematical, real number, 5.6 is actually not represented by a double exactly as 5.6 but as 5.599999...You could also do
which actually does yield
[5.0, 0.6]
.The
BigDecimal.valueOf
is in most JDK's (internally) implemented through a call toDouble.toString
however. But at least the string-related stuff doesn't clutter your code :-)Good follow-up question in comment:
The
Double.toString
method is actually very sophisticated. From the documentation ofDouble.toString
:The code for getting the characters
"5.6"
boils down toFloatingDecimal.getChars
: