由于对float或double 的使用不当,可能会出现精度丢失的问题。问题大概情况可以通过如下代码理解:
view plaincopy to clipboardprint?
public class FloatDoubleTest {
public static void main(String[] args) {
float f = 20014999;
double d = f;
double d2 = 20014999;
System.out.println("f=" + f);
System.out.println("d=" + d);
System.out.println("d2=" + d2);
}
}
public class FloatDoubleTest {
public static void main(String[] args) {
float f = 20014999;
double d = f;
double d2 = 20014999;
System.out.println("f=" + f);
System.out.println("d=" + d);
System.out.println("d2=" + d2);
}
}
得到的结果如下:
f=2.0015E7
d=2.0015E7
d2=2.0014999E7
从输出结果可以看出double 可以正确的表示20014999 ,而float 没有办法表示20014999 ,得到的只是一个近似值。这样的结果很让人讶异。20014999 这么小的数字在float下没办法表示。于是带着这个问题,做了一次关于float和double学习,做个简单分享,希望有助于大家对java 浮点数的理解。
关于 java 的 float 和 double
Java 语言支持两种基本的浮点类型: float 和 double 。java 的浮点类型都依据 IEEE 754 标准。IEEE 754 定义了32 位和 64 位双精度两种浮点二进制小数标准。
IEEE 754 用科学记数法以底数为 2 的小数来表示浮点数。32 位浮点数用 1 位表示数字的符号,用 8 位来表示指数,用 23 位来表示尾数,即小数部分。作为有符号整数的指数可以有正负之分。小数部分用二进制(底数 2 )小数来表示。对于64 位双精度浮点数,用 1 位表示数字的符号,用 11 位表示指数,52 位表示尾数。如下两个图来表示:
float(32位):
double(64位):
都是分为三个部分:
(1) 一个单独的符号位s 直接编码符号s 。
(2)k 位的幂指数E ,移码表示 。
(3)n 位的小数,原码表示 。
那么 20014999 为什么用 float 没有办法正确表示?
结合float和double的表示方法,通过分析 20014999 的二进制表示就可以知道答案了。
以下程序可以得出 20014999 在 double 和 float 下的二进制表示方式。
view plaincopy to clipboardprint?
public class FloatDoubleTest3 {
public static void main(String[] args) {
double d = 8;
long l = Double.doubleToLongBits(d);
System.out.println(Long.toBinaryString(l));
float f = 8;
int i = Float.floatToIntBits(f);
System.out.println(Integer.toBinaryString(i));
}
}
public class FloatDoubleTest3 {
public static void main(String[] args) {
double d = 8;
long l = Double.doubleToLongBits(d);
System.out.println(Long.toBinaryString(l));
float f = 8;
int i = Float.floatToIntBits(f);
System.out.println(Integer.toBinaryString(i));
}
}
输出结果如下:
Double:100000101110011000101100111100101110000000000000000000000000000
Float:1001011100110001011001111001100
对于输出结果分析如下。对于都不 double 的二进制左边补上符号位 0 刚好可以得到 64 位的二进制数。根据double的表示法,分为符号数、幂指数和尾数三个部分如下:
0 10000010111 0011000101100111100101110000000000000000000000000000
对于 float 左边补上符号位 0 刚好可以得到 32 位的二进制数。 根据float的表示法, 也分为 符号数、幂指数和尾数三个部分如下 :
0 10010111 00110001011001111001100
绿色部分是符号位,红色部分是幂指数,蓝色部分是尾数。
对比可以得出:符号位都是 0 ,幂指数为移码表示,两者刚好也相等。唯一不同的是尾数。
在 double 的尾数为: 001100010110011110010111 0000000000000000000000000000 ,省略后面的零,至少需要24位才能正确表示 。
而在 float 下面尾数为: 00110001011001111001100 ,共 23 位。
为什么会这样?原因很明显,因为 float尾数 最多只能表示 23 位,所以 24 位的 001100010110011110010111 在 float 下面经过四舍五入变成了 23 位的 00110001011001111001100 。所以 20014999 在 float 下面变成了 20015000 。
也就是说 20014999 虽然是在float的表示范围之内,但 在 IEEE 754 的 float 表示法精度长度没有办法表示出 20014999 ,而只能通过四舍五入得到一个近似值。
总结:
浮点运算很少是精确的,只要是超过精度能表示的范围就会产生误差。往往产生误差不是因为数的大小,而是因为数的精度。因此,产生的结果接近但不等于想要的结果。尤其在使用 float 和 double 作精确运算的时候要特别小心。
可以考虑采用一些替代方案来实现。如通过 String 结合 BigDecimal 或者通过使用 long 类型来转换。
分享到:
相关推荐
解决java数值范围以及float与double精度丢失的问题 Java中的数值范围和浮点数精度问题是许多开发者经常遇到的问题。下面我们将详细探讨Java中的数值范围、float和double类型的精度问题,并且提供解决方案。 一、...
5. **转换类型**:如果需要将`BigDecimal`转换回`double`或`float`,可以使用`doubleValue()`和`floatValue()`方法,但要注意这可能会丢失精度。 在实际开发中,`BigDecimal`的使用可以确保计算结果的准确无误,但...
这里有一个小知识:既然 float 和 double 型用来表示带有小数点的数,那为什么我们不称 它们为“小数”或者“实数”,要叫浮点数呢?因为这些数都以科学计数法的形式存储。当一个数如 50.534,转换成科学计数法的...
这个问题其实不是JAVA的bug,因为计算机本身是二进制的,而浮点数实际上只是个近似值,所以从二进制转化为十进制浮点数时,精度容易丢失,导致精度下降。 要保证精度就要使用BigDecimal类,而且不能直接从double...
常见问题还包括在进行浮点数运算时可能出现的精度丢失,以及在设计数据库表结构时选择合适的数据类型。例如,如果业务逻辑要求精确存储和计算小数,那么选择DECIMAL而非FLOAT或DOUBLE会更为合适。 总的来说,理解和...
在Java中,当从float强制转换为double时,理论上数值不会改变,但由于初始的存储精度限制,可能存在微小的误差。这种误差在处理大量浮点运算或高精度要求的应用中是需要特别注意的,因为它可能会影响计算结果的准确...
这种方式可能会导致数据丢失或精度下降,因此应谨慎使用。例如,将`int`类型的变量转换为`byte`类型: ```java int i = 100; byte b = (byte) i; // 强制类型转换 ``` ##### 4.3 包装类过渡类型转换 Java提供了六...
而显式转换(类型强制转换)需要程序员通过强制类型转换符 `(类型)` 来完成,如 `(int)doubleValue` 将double转换为int,这种转换可能会丢失数据,因此需要谨慎使用。 4. **自动装箱与拆箱的注意事项**: - 装箱和...
尽管`double`类型能够提供比`float`类型更高的精度,但在实际应用中,由于浮点数的二进制表示方式,有时仍然会出现精度丢失的问题。尤其在进行数学运算或比较时,这种精度问题可能会导致不准确的结果。 ### 保留两...
在 Java 中,int 类型的变量可以被强制转换为 short 类型的变量,但是需要注意的是,如果 int 类型的值超出了 short 类型的范围,将会导致精度丢失。例如: short shortvar = 0; int intvar = 0; intvar = shortvar...
同时,在处理非常小的数值时,确保不小于`float`或`double`的最小值,可以防止因为数值过小而导致的精度丢失。 此外,浮点数的比较也需要注意,由于浮点数的不精确性,直接使用`==`进行比较可能会导致错误的结果。...
注意:强制类型转换可能会导致数据丢失或精度降低。 #### 四、对象类型的转换 在 Java 中,对象类型的转换主要包括以下两种情况: 1. **父类到子类**:从父类引用到子类对象的转换需要显式地使用强制类型转换。 ...
例如,要将 float 转换为 double,可以先创建 `Float` 对象,再调用 `doubleValue()` 方法: ```java float f1 = 100.00f; Float F1 = new Float(f1); Double d1 = F1.doubleValue(); ``` 另一方面,将 double ...
例如,将`double`转换为`int`,可能丢失小数部分。转换时,数值可能超出目标类型能表示的范围,导致数据溢出,或者精度降低。例如,将`double`类型的3.10转换为`int`,结果为3,因为小数部分被丢弃。对于整数之间的...
// 高位的位数会被截断,可能会导致精度丢失 ``` 最后,我们要理解“把大类型转化为小的类型时可能会丢失”。当一个大数值类型(如long或double)转换为一个小数值类型(如byte、short或int)时,如果原始值超过了...
在Java中,简单数据类型包括byte、short、char、int、long、float和double。转换遵循从低精度到高精度的自动转换和从高精度到低精度的强制转换规则。 - 自动类型转换:低精度类型可以自动转换为高精度类型,例如...
数值型又细分为整型(byte、short、int、long)和浮点型(float、double)。此外,还有特殊的类类型如String和Date。 1. **数据类型转换的种类** - **简单数据类型之间的转换**:包括整型、浮点型和字符型之间的...
- float可以自动转换为double,但double不能自动转换为float,因为double的精度更高。 - 强制类型转换可能导致精度损失,如`(float) doubleValue`。 3. 字符型和数值型之间的转换: - char可以转换为int,因为它...
但是,从double到float转换时,可能因精度丢失而改变数值。 六、字符与字符串转换 1. char转String:可以使用String类的构造函数,如String str = new String(new char[]{'c', 'h', 'a', 'r'});。 2. String转char...
例如,将float转换为double,可以先创建一个Float对象,再调用其doubleValue()方法:`float f = 100.00f; Float F = new Float(f); double d = F.doubleValue();` 2. **字符串与其他数据类型的转换** - **字符串转...