学习JAVA浮点数必看文章:转自
http://www.phpcsdn.com/club/space.php?uid=3&do=thread&id=693
虽然几乎每种处理器和编程语言都支持浮点运算,但大多数程序员很少注意它。这容易理解 ― 我们中大多数很少需要使用非整数类型。除了科学计算和偶尔的计时测试或基准测试程序,其它情况下几乎都用不着它。同样,大多数开发人员也容易忽略 java.math.BigDecimal 所提供的任意精度的小数 ― 大多数应用程序不使用它们。然而,在以整数为主的程序中有时确实会出人意料地需要表示非整型数据。例如,JDBC 使用 BigDecimal 作为 SQL DECIMAL 列的首选互换格式。
【IEEE 浮点】
Java 语言支持两种基本的浮点类型: float 和 double ,以及与它们对应的包装类 Float 和 Double 。它们都依据 IEEE 754 标准,该标准为 32 位浮点和 64 位双精度浮点二进制小数定义了二进制标准。
IEEE 754 用科学记数法以底数为 2 的小数来表示浮点数。IEEE 浮点数用 1 位表示数字的符号,用 8 位来表示指数,用 23 位来表示尾数,即小数部分。作为有符号整数的指数可以有正负之分。小数部分用二进制(底数 2)小数来表示,这意味着最高位对应着值 ?(2 -1),第二位对应着 ?(2 -2),依此类推。对于双精度浮点数,用 11 位表示指数,52 位表示尾数。IEEE 浮点值的格式如图 1 所示。
因为用科学记数法可以有多种方式来表示给定数字,所以要规范化浮点数,以便用底数为 2 并且小数点左边为 1 的小数来表示,按照需要调节指数就可以得到所需的数字。所以,例如,数 1.25 可以表示为尾数为 1.01,指数为 0: (-1) 0*1.01 2*2 0
数 10.0 可以表示为尾数为 1.01,指数为 3: (-1) 0*1.01 2*2 3
【特殊数字】
除了编码所允许的值的标准范围(对于 float ,从 1.4e-45 到 3.4028235e+38),还有一些表示无穷大、负无穷大、 -0 和 NaN(它代表“不是一个数字”)的特殊值。这些值的存在是为了在出现错误条件(譬如算术溢出,给负数开平方根,除以 0 等)下,可以用浮点值集合中的数字来表示所产生的结果。
这些特殊的数字有一些不寻常的特征。例如, 0 和 -0 是不同值,但在比较它们是否相等时,被认为是相等的。用一个非零数去除以无穷大的数,结果等于 0 。特殊数字 NaN 是无序的;使用 == 、 < 和 > 运算符将 NaN 与其它浮点值比较时,结果为 false 。如果 f 为 NaN,则即使 (f == f) 也会得到 false 。如果想将浮点值与 NaN 进行比较,则使用 Float.isNaN() 方法。表 1 显示了无穷大和 NaN 的一些属性。
【表 1. 特殊浮点值的属性】
表达式 结果
Math.sqrt(-1.0) -> NaN
0.0 / 0.0 -> NaN
1.0 / 0.0 -> 无穷大
-1.0 / 0.0 -> 负无穷大
NaN + 1.0 -> NaN
无穷大 + 1.0 -> 无穷大
无穷大 + 无穷大 -> 无穷大
NaN > 1.0 -> false
NaN == 1.0 -> false
NaN < 1.0 -> false
NaN == NaN -> false
0.0 == -0.01 -> true
基本浮点类型和包装类浮点有不同的比较行为
使事情更糟的是,在基本 float 类型和包装类 Float 之间,用于比较 NaN 和 -0 的规则是不同的。对于 float 值,比较两个 NaN 值是否相等将会得到 false ,而使用 Float.equals() 来比较两个 NaN Float 对象会得到 true 。造成这种现象的原因是,如果不这样的话,就不可能将 NaN Float 对象用作 HashMap 中的键。类似的,虽然 0 和 -0 在表示为浮点值时,被认为是相等的,但使用 Float.compareTo() 来比较作为 Float 对象的 0 和 -0 时,会显示 -0 小于 0 。
【浮点中的危险】
由于无穷大、NaN 和 0 的特殊行为,当应用浮点数时,可能看似无害的转换和优化实际上是不正确的。例如,虽然好象 0.0-f 很明显等于 -f ,但当 f 为 0 时,这是不正确的。还有其它类似的 gotcha,表 2 显示了其中一些 gotcha。
表 2. 无效的浮点假定
这个表达式…… 不一定等于…… 当……
0.0 - f -f f 为 0
f < g ! (f >= g) f 或 g 为 NaN
f == f true f 为 NaN
f + g - g f g 为无穷大或 NaN
舍入误差
浮点运算很少是精确的。虽然一些数字(譬如 0.5 )可以精确地表示为二进制(底数 2)小数(因为 0.5 等于 2 -1),但其它一些数字(譬如 0.1 )就不能精确的表示。因此,浮点运算可能导致舍入误差,产生的结果接近 ― 但不等于 ― 您可能希望的结果。例如,下面这个简单的计算将得到 2.600000000000001 ,而不是 2.6 :
double s=0;
for (int i=0; i<26; i++)
s += 0.1;
System.out.println(s);
类似的, .1*26 相乘所产生的结果不等于 .1 自身加 26 次所得到的结果。当将浮点数强制转换成整数时,产生的舍入误差甚至更严重,因为强制转换成整数类型会舍弃非整数部分,甚至对于那些“看上去似乎”应该得到整数值的计算,也存在此类问题。例如,下面这些语句:
double d = 29.0 * 0.01;
System.out.println(d);
System.out.println((int) (d * 100));
将得到以下输出:
0.29
28
这可能不是您起初所期望的。
【浮点数比较指南】
由于存在 NaN 的不寻常比较行为和在几乎所有浮点计算中都不可避免地会出现舍入误差,解释浮点值的比较运算符的结果比较麻烦。
最好完全避免使用浮点数比较。当然,这并不总是可能的,但您应该意识到要限制浮点数比较。如果必须比较浮点数来看它们是否相等,则应该将它们差的绝对值同一些预先选定的小正数进行比较,这样您所做的就是测试它们是否“足够接近”。(如果不知道基本的计算范围,可以使用测试“abs(a/b - 1) < epsilon”,这种方法比简单地比较两者之差要更准确)。甚至测试看一个值是比零大还是比零小也存在危险 ―“以为”会生成比零略大值的计算事实上可能由于积累的舍入误差会生成略微比零小的数字。
NaN 的无序性质使得在比较浮点数时更容易发生错误。当比较浮点数时,围绕无穷大和 NaN 问题,一种避免 gotcha 的经验法则是显式地测试值的有效性,而不是试图排除无效值。在清单 1 中,有两个可能的用于特性的 setter 的实现,该特性只能接受非负数值。第一个实现会接受 NaN,第二个不会。第二种形式比较好,因为它显式地检测了您认为有效的值的范围。
清单 1. 需要非负浮点值的较好办法和较差办法
// Trying to test by exclusion -- this doesn't catch NaN or infinity
public void setFoo(float foo) {
if (foo < 0)
throw new IllegalArgumentException(Float.toString(f));
this.foo = foo;
}
// Testing by inclusion -- this does catch NaN
public void setFoo(float foo) {
if (foo >= 0 && foo < Float.INFINITY)
this.foo = foo;
else
throw new IllegalArgumentException(Float.toString(f));
}
不要用浮点值表示精确值
一些非整数值(如几美元和几美分这样的小数)需要很精确。浮点数不是精确值,所以使用它们会导致舍入误差。因此,使用浮点数来试图表示象货币量这样的精确数量不是一个好的想法。使用浮点数来进行美元和美分计算会得到灾难性的后果。浮点数最好用来表示象测量值这类数值,这类值从一开始就不怎么精确。
【用于较小数的 BigDecimal】
从 JDK 1.3 起,Java 开发人员就有了另一种数值表示法来表示非整数: BigDecimal 。 BigDecimal 是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。在内部,可以用任意精度任何范围的值和一个换算因子来表示 BigDecimal ,换算因子表示左移小数点多少位,从而得到所期望范围内的值。因此,用 BigDecimal 表示的数的形式为 unscaledValue*10 -scale 。
用于加、减、乘和除的方法给 BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。因此,因为创建对象的开销, BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。如果您正在寻找一种能精确表示如货币量这样的数值,则 BigDecimal 可以很好地胜任该任务。
所有的 equals 方法都不能真正测试相等
如浮点类型一样, BigDecimal 也有一些令人奇怪的行为。尤其在使用 equals() 方法来检测数值之间是否相等时要小心。 equals() 方法认为,两个表示同一个数但换算值不同(例如, 100.00 和 100.000 )的 BigDecimal 值是不相等的。然而, compareTo() 方法会认为这两个数是相等的,所以在从数值上比较两个 BigDecimal 值时,应该使用 compareTo() 而不是 equals() 。
另外还有一些情形,任意精度的小数运算仍不能表示精确结果。例如, 1 除以 9 会产生无限循环的小数 .111111... 。出于这个原因,在进行除法运算时, BigDecimal 可以让您显式地控制舍入。 movePointLeft() 方法支持 10 的幂次方的精确除法。
使用 BigDecimal 作为互换类型
SQL-92 包括 DECIMAL 数据类型,它是用于表示定点小数的精确数字类型,它可以对小数进行基本的算术运算。一些 SQL 语言喜欢称此类型为 NUMERIC 类型,其它一些 SQL 语言则引入了 MONEY 数据类型,MONEY 数据类型被定义为小数点右侧带有两位的小数。
如果希望将数字存储到数据库中的 DECIMAL 字段,或从 DECIMAL 字段检索值,则如何确保精确地转换该数字?您可能不希望使用由 JDBC PreparedStatement 和 ResultSet 类所提供的 setFloat() 和 getFloat() 方法,因为浮点数与小数之间的转换可能会丧失精确性。相反,请使用 PreparedStatement 和 ResultSet 的 setBigDecimal()及 getBigDecimal() 方法。
对于 BigDecimal ,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用 BigDecimal(double) 构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。
构造 BigDecimal 数
对于 BigDecimal ,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用 BigDecimal(double) 构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。
如果使用 BigDecimal(double) 构造函数不恰当,在传递给 JDBC setBigDecimal() 方法时,会造成似乎很奇怪的 JDBC 驱动程序中的异常。例如,考虑以下 JDBC 代码,该代码希望将数字 0.01 存储到小数字段:
PreparedStatement ps = connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");
ps.setString(1, "penny");
ps.setBigDecimal(2, new BigDecimal(0.01));
ps.executeUpdate();
在执行这段似乎无害的代码时会抛出一些令人迷惑不解的异常(这取决于具体的 JDBC 驱动程序),因为 0.01 的双精度近似值会导致大的换算值,这可能会使 JDBC 驱动程序或数据库感到迷惑。JDBC 驱动程序会产生异常,但可能不会说明代码实际上错在哪里,除非意识到二进制浮点数的局限性。相反,使用 BigDecimal("0.01") 或 BigDecimal(1, 2) 构造 BigDecimal 来避免这类问题,因为这两种方法都可以精确地表示小数。
【结束语】
在 Java 程序中使用浮点数和小数充满着陷阱。浮点数和小数不象整数一样“循规蹈矩”,不能假定浮点计算一定产生整型或精确的结果,虽然它们的确“应该”那样做。最好将浮点运算保留用作计算本来就不精确的数值,譬如测量。如果需要表示定点数(譬如,几美元和几美分),则使用 BigDecimal 。
//////////////////////////////////////////////////
以下关于浮点数的内容转自
http://www.balingke.com/archives/737.html
与整型数据不同,浮点数是采用"符号位+指数+有效位(尾数)"的存储方式,介于这种存储的特殊性,所以只占4字节的float类型数据却可以比占了8字节的long类型数据有着更大的取值范围。然而,这也是要付出一定的代价的,具体情况如下。
(1)浮点数只是近似的存储
【浮点数缺陷1】
public class FloatTest1 {
public static void main(String[] args) {
int intMax = Integer.MAX_VALUE;
float fintMax = intMax;
int intMin = Integer.MIN_VALUE;
float fintMin = intMin;
long longMax = Long.MAX_VALUE;
double dlongMax = longMax;
long longMin = Long.MIN_VALUE;
double dlongMin = longMin;
System.out.println("int类型的最大值是:" + intMax);
System.out.println("使用float存储后为:" + fintMax);
System.out.println("int类型的最小值是:" + intMin);
System.out.println("使用float存储后为:" + fintMin);
System.out.println("long类型的最大值是:" + longMax);
System.out.println("使用float存储后为:" + dlongMax);
System.out.println("long类型的最小值是:" + longMin);
System.out.println("使用float存储后为:" + dlongMin);
}
}
// 运行结果:
// int类型的最大值是:2147483647
// 使用float存储后为:2.14748365E9
// int类型的最小值是:-2147483648
// 使用float存储后为:-2.14748365E9
// long类型的最大值是:9223372036854775807
// 使用float存储后为:9.223372036854776E18
// long类型的最小值是:-9223372036854775808
// 使用float存储后为:-9.223372036854776E18
从结果为我们得知,无论是使用float类型的变量来存储数值,还是用double类型的变量来存储数值,都损失了一定的精度,在使用浮点数来输出时,都未能输出准确的结果。
【浮点数的缺陷2】测试一下用浮点数来存储较小的数据。
public class FloatTest2 {
public static void main(String[] args) {
for(float f = 0.1f; f < 1; f += 0.1){
System.out.println(f);
}
for(double d = 0.1f; d < 1; d += 0.1){
System.out.println(d);
}
}
}
// 运行结果:
// 0.1
// 0.2
// 0.3
// 0.4
// 0.5
// 0.6
// 0.70000005
// 0.8000001
// 0.9000001
// 0.10000000149011612
// 0.20000000149011612
// 0.30000000149011613
// 0.40000000149011616
// 0.5000000014901161
// 0.6000000014901161
// 0.7000000014901161
// 0.8000000014901161
// 0.900000001490116
从结果可知,虽然在计算的时候每次递增0.1,然而结果却是如此的不尽人意。从两个例子可以得出:不论是存储达的数值,还是小的数值,浮点数都不是十分的准确。浮点数在计算机中只是近似的存储,类似于1/3这样的无限小数,用浮点数也不能准确地表示出来。这里需要注意的是,在第二个例子中,我们看到0.1~0.6的输出结果是正确的,而从0.7开始才出现误差。其实是我们的眼睛受骗了。
在执行float f=0.1f时,计算机中的数据并不是纯粹的0.1,而是类似于0.1000~000xxx的形式,其中000~000表示若干个0,xxx表示若干个数字,假设其数据为0.1000000000111111,而folat类型只能保留7~8位有效数字,这样就会从中间截断,从而float存储的值成为0.10000000,即浮点数值0.1。
(2)浮点数的大小比较
public class FloatTest3 {
public static void main(String[] args) {
double d1 = 0.1;
double d2 = 0.2;
double d3 = d1 + d2;
if (d1 + d2 == 0.3) {
System.out.println("d1 + d2 == 0.3");
} else {
System.out.println("d1 + d2 != 0.3");
System.out.println(d3);
}
}
}
// 运行结果:
// d1 + d2 != 0.3
// 0.30000000000000004
再次说明,浮点数的存储是有一定的误差的,所以,不要用浮点数进行相等的比较,也不要进行混合比较(如int与float的比较)。
(3)数量级相差很大时浮点数的运算
在使用浮点数时,还需要注意一点,那就是不要再数量级相差太大的数之间进行加减运算,这样可能无法改变原数量级较大的操作数。情况下例:
public class FloatTest4 {
public static void main(String[] args) {
float f1 = 16777216f;
float f2 = f1 + 1;
if(f1 == f2){
System.out.println("f1等于f2");
} else {
System.out.println("f1不等于f2");
}
System.out.println("f1 = " + f1);
System.out.println("f2 = " + f2);
}
}
// 运行结果:
// f1等于f2
// f1 = 1.6777216E7
// f2 = 1.6777216E7
这与浮点数的内部表示有关,这里可以不必理会浮点数复杂的存储原理,只要明白浮点数毕竟只是近似的存储,而且其存储值越大时,损失的精度也越大,对其进行数量级相差较大的加减元素按就会得不到想要的结果。
- 大小: 4.2 KB
分享到:
相关推荐
浮点数(单精度浮点数,双精度浮点数) 浮点数是一种数字表示方法,用于近似表示任意实数。在计算机中,浮点数由一个整数或定点数(即尾数)乘以某个基数(通常是 2)的整数次幂得到。这种表示方法类似于基数为 10 ...
`float_2_char.zip` 提供的代码着重解决了单片机中浮点数(`float`)与字符型(`char`)之间的转换问题,这对于节省存储空间、提高通信效率以及降低错误率非常有用。下面将详细介绍浮点数与字符型之间的转换原理和...
此外,当涉及到32位整数和32位浮点数之间的转换时,整数转浮点数时可能会因为浮点数的精度而产生非预期的结果,而浮点数转整数时可能因四舍五入规则产生误差。因此,在编写程序时,应充分考虑这些潜在问题,并进行...
本文将深入探讨十六进制到浮点数的转换,特别是在LabVIEW环境下的实现。LabVIEW(Laboratory Virtual Instrument Engineering Workbench)是由美国国家仪器公司(NI)开发的一种图形化编程语言,广泛应用于工程、...
在数字信号处理领域,浮点数运算在许多高级计算任务中扮演着重要角色。TMS320C3x是一款由德州仪器(TI)推出的数字信号处理器(DSP),它支持浮点运算,使得复杂的数学计算得以高效执行。本文将深入探讨TMS320C3x中...
浮点数转换在计算机科学中是一项基础且重要的概念,特别是在编程和数字处理领域。浮点数,顾名思义,是指可以表示小数部分的数值,与整数相对。它们在计算机内部通常以二进制浮点数的形式存储,遵循IEEE 754标准。这...
在C语言中,将浮点数转换为字符串是一项常见的任务,尤其在需要将数值数据输出到文件或屏幕上时。这个过程通常涉及到`printf`函数家族的使用,它们能够按照指定的格式将各种类型的数据转化为可读的字符串。本文将...
本文将详细介绍如何使用S7-200SMART进行双精度浮点数到单精度浮点数的转换,并提供相应的库文件及使用说明。 1. **浮点数类型**: 浮点数在计算机中分为单精度浮点数(32位,IEEE 754标准)和双精度浮点数(64位,...
在三菱PLC编程中,浮点数运算指令是非常重要的功能,因为工业控制中的许多参数和变量都需要进行浮点数运算。在三菱PLC中,浮点数运算指令能够处理32位的浮点数,并进行比较、变换、四则运算、开方运算以及三角函数...
JavaScript实现浮点数转十六进制字符的过程涉及到了浮点数的表示、IEEE 754标准、二进制与十六进制的转换等多个知识点。由于JavaScript直接使用浮点数转十六进制的功能实现并不直接,因此需要借助其他方法来实现。...
在处理数值计算时,有时我们需要将不同精度的浮点数进行转换,如将64位浮点数转换为32位浮点数。这个过程涉及到浮点数的表示方式、数据类型的转换以及可能的精度损失问题。 64位浮点数,也称为双精度浮点数(Double...
在计算机科学中,十六进制(Hexadecimal)和浮点数(Floating Point)是两种不同的数值表示方式。十六进制是一种逢16进1的数制,常用于编程和计算机内存中表示数据,因为它可以更紧凑地表示二进制数。而浮点数则是一...
浮点数与十六进制转换在计算机科学,尤其是单片机编程中,是常见的数据处理操作。浮点数是一种表示实数的方式,它包括一个符号位、指数部分和尾数部分,而十六进制(Hexadecimal)是数字系统中的一种表示方式,常...
在计算机科学中,浮点数是一种用于表示数值的近似方式,主要分为单精度浮点数和双精度浮点数。这些数据类型广泛应用于各种计算,特别是在需要处理大量精确度和范围的数学运算中,例如科学计算、图像处理和游戏开发。...
在上述文档中,我们看到的是如何将浮点数在库卡机器人和PLC之间进行转换和通信的过程。浮点数是一种用于表示带有小数的数值类型,通常在计算和精度要求较高的应用中使用。以下是对这一过程的详细说明: 1. **变量...
标题中的“HEX与浮点数相互转换”指的是在计算机科学中处理数字时的一个常见操作。HEX(十六进制)是计算机中广泛使用的数字表示方式,尤其在内存和CPU指令中,而浮点数则用于表示带有小数部分的数值,常用于科学...
IEEE754浮点数标准是计算机科学中用于表示和操作浮点数的国际标准,由电气和电子工程师协会(IEEE)制定。这个标准对于数字计算的精确性和可移植性至关重要,广泛应用于现代计算机系统、编程语言以及硬件处理器。在...
浮点数与二进制转换在计算机科学中是至关重要的概念,特别是在通信开发、数据存储和计算领域。本文将深入探讨浮点数和二进制之间的转换,以及如何利用工具进行这种转换。 浮点数是一种表示实数的方式,它允许在有限...
本篇将详细讲解如何在TIA博途V17版本中,利用全局功能块(FC,Function Block)库文件实现对浮点数的截取,并自定义小数点后的位数。 首先,浮点数在PLC编程中常用于处理需要较高精度的数值运算。在TIA博途V17中,...
浮点数在计算机科学中扮演着至关重要的角色,特别是在数值计算、图形处理和科学计算等领域。浮点数的表示方式是基于二进制的,但为了方便人类阅读和理解,通常我们使用十进制形式。浮点数与二进制之间的转换是理解和...