文章目录
“计算机程序的错误通常在于数值近似。”
前言
这里是分享 Java 相关内容的专刊,每日一更。
本期将为大家带来以下内容:
- Java 的浮点数类型
- 基本的浮点数运算符
- 浮点数除法与取模
- 自增与自减运算
- 浮点数的科学计数法表示
- 浮点数的精度问题
- 解决浮点数精度问题
- 浮点数溢出问题
- 类型自动提升(Type Promotion)
- 强制类型转换(Type Casting)
Java 的浮点数类型
Java 是一种强类型语言,变量在声明时必须指定类型。对于浮点数运算,Java 提供了以下几种基本数据类型:
类型 | 大小 | 精度(有效位数) | 最小值 | 最大值 |
---|---|---|---|---|
float | 4 字节 | 大约 7 位有效数字 | 1.4 × 1 0 − 45 1.4 \times 10^{-45} 1.4×10−45 | 3.4 × 1 0 38 3.4 \times 10^{38} 3.4×1038 |
double | 8 字节 | 大约 15 位有效数字 | 4.9 × 1 0 − 324 4.9 \times 10^{-324} 4.9×10−324 | 1.7 × 1 0 308 1.7 \times 10^{308} 1.7×10308 |
float
:单精度浮点数,使用 32 位来存储,适合对内存要求较高或精度要求较低的场景。double
:双精度浮点数,使用 64 位来存储,是 Java 中的默认浮点数类型,适用于大多数科学计算和高精度运算。
通常,Java 默认将小数类型识别为 double
。如果需要定义 float
类型的数值,必须在数值后加上 f
或 F
后缀。
基本的浮点数运算符
Java 中的基本整数运算符与其他主流编程语言类似,支持的操作包括加、减、乘、除等。以下是 Java 支持的整数运算符:
+
:加法-
:减法*
:乘法/
:除法%
:取模(求余)
double a = 5.7;
double b = 2.3;
System.out.println("加法: " + (a + b)); // 输出: 8.0
System.out.println("减法: " + (a - b)); // 输出: 3.4
System.out.println("乘法: " + (a * b)); // 输出: 13.11
System.out.println("除法: " + (a / b)); // 输出: 2.4782608695652173
System.out.println("取模: " + (a % b)); // 输出: 1.1000000000000005
浮点数除法与取模
浮点数除法不同于整数除法,它会保留小数部分。例如,10.0 / 3.0
的结果为 3.3333333333333335
。取模运算同样适用于浮点数,返回的是除法后的余数。
double result = 10.0 / 3.0; // 结果是 3.3333333333333335
double remainder = 10.0 % 3.0; // 余数是 1.0
自增与自减运算
Java 同样支持对浮点数的自增(++
)和自减(--
)操作。与整数类型类似,浮点数的自增自减操作也有前置与后置之分:
- 前置:先增加/减少,再返回值。
- 后置:先返回值,再增加/减少。
double a = 1.5;
double b = ++a; // 先将 a 加 1(a 变为 2.5),然后将结果赋值给 b,所以 b 的值为 2.5
System.out.println("前置自增后的 a: " + a); // 输出: 2.5
System.out.println("前置自增后的 b: " + b); // 输出: 2.5
double c = 1.5;
double d = c++; // 先将 c 的当前值(1.5)赋给 d,然后再将 c 加 1(c 变为 2.5)
System.out.println("后置自增后的 c: " + c); // 输出: 2.5
System.out.println("后置自增后的 d: " + d); // 输出: 1.5
double e = 2.5;
double f = --e; // 先将 e 减 1(e 变为 1.5),然后将结果赋值给 f,所以 f 的值为 1.5
System.out.println("前置自减后的 e: " + e); // 输出: 1.5
System.out.println("前置自减后的 f: " + f); // 输出: 1.5
double g = 2.5;
double h = g--; // 先将 g 的当前值(2.5)赋给 h,然后再将 g 减 1(g 变为 1.5)
System.out.println("后置自减后的 g: " + g); // 输出: 1.5
System.out.println("后置自减后的 h: " + h); // 输出: 2.5
浮点数的科学计数法表示
Java 支持用科学计数法表示浮点数,尤其适合表示非常大的或非常小的数。例如,1.23e10
代表
1.23
×
1
0
10
1.23 \times 10^{10}
1.23×1010,1.23e-10
代表
1.23
×
1
0
−
10
1.23 \times 10^{-10}
1.23×10−10。
double large = 1.23e10; // 1.23 * 10^10
double small = 1.23e-10; // 1.23 * 10^-10
System.out.println("large = " + large); // 输出: 1.23E10
System.out.println("small = " + small); // 输出: 1.23E-10
浮点数的精度问题
浮点数的精度是一个常见问题,尤其是在处理非常小的数或无法精确表示的数时(如 0.1 和 0.2)。由于浮点数是以二进制表示的,因此某些十进制的小数在浮点数中无法精确表示,可能会导致精度丢失。
double a = 0.1;
double b = 0.2;
System.out.println("0.1 + 0.2 = " + (a + b)); // 输出: 0.30000000000000004
解决浮点数精度问题
在一些对精度要求很高的场景中(如金融计算),可以使用 BigDecimal
类来避免浮点数的精度问题。
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println("0.1 + 0.2 = " + a.add(b)); // 输出: 0.3
浮点数溢出问题
浮点数虽然范围很大,但仍然有极限。当浮点数超出其表示范围时,会出现溢出问题。Java 对于溢出的处理方式不同于整数,它会返回 Infinity
或 -Infinity
,而不是像整数那样回绕。
double large = 1.7E308;
double result = large * 10;
System.out.println("Overflow result: " + result); // 输出: Infinity
类型自动提升(Type Promotion)
类型自动提升是指在表达式中,当参与运算的不同数据类型不一致时,Java 自动将较小的类型提升为较大的类型,以避免数据丢失。通常,这种提升遵循从低精度到高精度的顺序:
byte → short → int → long → float → double
float f = 2.5f;
int i = 10;
double result = f + i; // int 自动提升为 float,然后结果再提升为 double
System.out.println("运算结果: " + result); // 输出: 12.5
在上述示例中,int
类型的 i
自动提升为 float
。
强制类型转换(Type Casting)
强制类型转换是将一个数据类型的值显式转换为另一个类型的过程。这通常用于将高精度的类型转换为低精度类型(比如将 double
转换为 int
),或者在需要进行特定的类型转换时使用。强制转换可能会导致数据丢失,因此需要格外小心。
double d = 5.99;
int i = (int) d; // 强制类型转换为 int,舍去小数部分
System.out.println("强制转换结果: " + i); // 输出: 5
本期小知识
尽量避免在循环控制、比较等对精度要求较高的场景下使用浮点数,因为它们的精度限制和误差可能导致意外行为。在这种情况下,使用整数类型或 BigDecimal 会更安全。