乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Java概述与环境搭建

    • Java概述与环境搭建
    • Java语言概述
    • 解释型语言与编译型语言对比
    • JDK安装与配置
    • JDK、JRE、JVM 详解
    • HelloWorld程序详解
    • IDE 介绍
  • 第2章 标识符与基本数据类型

    • 章节导读
    • 变量概述
    • 常量概述
    • 基本类型与包装类
    • 字节型 byte
    • 短整型 short
    • 整型 int
    • 长整型 long
    • 单精度浮点型 float
    • 双精度浮点型 double
    • 字符型 char
    • 布尔型 boolean
    • 类型转换
  • 第3章 运算符与表达式

    • 章节导读
    • 算术运算符
    • 赋值运算符
    • 关系运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 运算符优先级
    • 表达式
  • 第4章 流程控制

    • 章节导读
    • 常见的程序运行流程
    • if-else 选择结构
    • switch 多分支选择
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
  • 第5章 数组

    • 章节导读
    • 一维数组
    • 多维数组
    • Arrays 工具类
  • 第6章 类与对象

    • 章节导读
    • 类与对象
    • 方法定义与调用
    • 构造方法
    • 封装
    • 访问修饰符
    • package 与 import
    • static 关键字
    • this 关键字
    • 参数传递 详解
    • 枚举
    • 成员内部类
    • 局部内部类
    • 静态内部类
    • 匿名内部类
  • 第7章 接口与继承

    • 章节导读
    • 继承
    • super 关键字
    • final 关键字
    • 多态
    • 向上转型与向下转型
    • 抽象类
    • 接口
    • 抽象类与接口对比
  • 第8章 注解

    • 章节导读
    • 注解基础
    • 元注解详解
    • 自定义注解
  • 第9章 常用类

    • 章节导读:Java 常用类
    • Object 类:万类之祖
    • 包装类:基本类型的对象化
    • String:不可变的字符串
    • StringBuffer:线程安全的可变字符串
    • StringBuilder:可变的字符串构建器
    • Math:数学运算工具类
    • Random:伪随机数生成器
    • 大数值运算 详解
    • 日期时间API 详解
  • 第10章 异常机制

    • 章节导读
    • 异常体系与分类
    • try-catch-finally
    • try-with-resources
    • throws 与 throw
    • 自定义异常
  • 第11章 泛型

    • 章节导读
    • 泛型基础
    • 通配符与PECS原则
    • 类型擦除
  • 第12章 集合框架

    • 章节导读
    • 集合框架概述
    • ArrayList
    • LinkedList
    • HashMap 详解
    • LinkedHashMap 详解
    • TreeMap 详解
    • HashSet
    • TreeSet 详解
    • TreeSet 与 Comparable
    • Collections 工具类详解
  • 第13章 IO流

    • 章节导读
    • IO流概述
    • 字节流
    • 字符流
    • 缓冲流
    • 转换流 详解
    • 序列化 详解
    • NIO与Files 详解
    • NIO与Files工具类
  • 第14章 多线程与并发

    • 第十六章 多线程与并发 —— 章节导读
    • 线程基础详解
    • synchronized 详解
    • Lock 与显式锁详解
    • volatile 详解
    • wait 与 notify 详解
    • ThreadLocal详解
    • 原子类详解
    • 并发工具类详解
    • 线程池详解
  • 第15章 反射

    • 章节导读
    • 反射概述与 Class 对象
    • Constructor 与对象创建
    • Field与Method详解
    • 反射应用详解
  • 第16章 JDK8新特性

    • 章节导读
    • Lambda 表达式
    • Stream API 基础
    • Stream API 高级详解
    • Optional 详解
    • 新日期时间API详解
  • 第17章 JDK9-11新特性

    • 章节导读
    • 模块化系统 — Project Jigsaw(JDK 9)
    • var 局部变量类型推断(JDK 10)
    • 集合工厂方法与增强(JDK 9 / 10 / 11)
    • 接口增强:private 方法(JDK 9)
    • Stream API 增强(JDK 9)
    • Optional 增强(JDK 9 / 10 / 11)
    • String 新增方法(JDK 11)
    • HTTP Client 与 Files 增强(JDK 11)
    • 直接运行 Java 源文件 — JEP 330(JDK 11)
  • 第18章 JDK12-17新特性

    • 章节导读
    • Switch 表达式(JDK 12 预览 / JDK 14 正式)
    • 文本块 Text Blocks(JDK 13 预览 / JDK 15 正式)
    • Records 记录类(JDK 14 预览 / JDK 16 正式)
    • 密封类 Sealed Classes(JDK 15 预览 / JDK 17 正式)
    • instanceof 模式匹配(JDK 14 预览 / JDK 16 正式)
    • Switch 模式匹配 — Pattern Matching for switch(JDK 17 预览 / JDK 21 正式)
    • Helpful NPE 与 String 增强(JDK 12 / JDK 14 / JDK 15)
    • Stream 增强(JDK 12 / JDK 16)
    • 日期时间增强 — Day Period 支持(JDK 16)
  • 第19章 JDK18-21新特性

    • 章节导读
    • 虚拟线程(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 序列集合(JDK 21 正式)
    • Switch 模式匹配(JDK 17 预览 / JDK 18 第二预览 / JDK 20 第四预览 / JDK 21 正式)
    • Record 模式匹配(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 未命名模式与变量(JDK 21 预览 / JDK 22 正式)
  • 第20章 JDK 22-25 新特性

    • 章节导读
    • 字符串模板(JDK 22 预览 / JDK 23 第二预览 / JDK 24 第三预览)
    • Stream Gatherers(JDK 22 预览 / JDK 24 第二预览)
    • 隐式声明类与实例方法(JDK 23 预览 / JDK 24 第二预览)
    • 原始类型模式匹配(JDK 24 预览)
  • 附录

    • Java 核心知识点
    • Java SE 专业术语
    • Java特性索引(JDK 8 → 25)

单精度浮点型 float

本章定位:float是32位IEEE 754单精度浮点数,有效数字约7位十进制数。在飞翔科技的电商和工资系统中,商品价格188.88f、基本工资8888.00f、气温26.5f等都用float表示。白歌提醒小崔:"浮点数天生不精确,算钱记得用BigDecimal——但面试考float,你得把IEEE 754的存储格式讲清楚。"


定义与语法

属性说明
关键字float(单精度浮点型)
位数32 bits(4字节)
标准IEEE 754 单精度浮点数
有效数字约6~7位十进制数字
取值范围(正数)约 1.4×10⁻⁴⁵ ~ 3.4×10³⁸
默认值0.0f
包装类java.lang.Float
字面量后缀必须加 f 或 F(浮点字面量默认是double)

基本声明

float price = 188.88f;             // 商品单价
float discount = 88.8f;            // 折扣价
float shippingFee = 8.8f;          // 运费
float basicSalary = 8888.00f;      // 基本工资
float morningTemp = 26.5f;         // 早晨气温
float bodyTemp = 36.5f;            // 体温
float ticketPrice = 100.00f;       // 门票价格
float drugPrice = 23.20f;          // 药品价格

深度原理:IEEE 754 单精度的二进制存储

float的32位布局

计算公式:

value = (-1)^S × (1.M) × 2^(E-127)

特殊情况:
- E=0,   M=0   → ±0
- E=0,   M≠0   → 非规格化数(subnormal)
- E=255, M=0   → ±∞(无穷大)
- E=255, M≠0  → NaN(Not a Number)

示例:188.88f的实际存储

188.88 的二进制转换过程:

1. 188的二进制:10111100
2. 0.88的二进制:0.11100001010001111010111...(无限循环!)

188.88 ≈ 1.0111100011100001010001111 × 2^7

符号位 S = 0(正数)
指数位 E = 7 + 127 = 134 = 10000110
尾数位 M = 01111000111000010100011(取前23位)

最终 32 位:0 10000110 01111000111000010100011

关键认知:0.88无法用二进制精确表示,就像1/3无法用十进制精确表示。float存储的是近似值,这就是浮点数精度问题的数学根源。


完整代码示例

示例一:飞翔商城购物结算

/**
 * 飞翔科技 - float 类型演示
 * 场景:飞翔商城购物车结算,使用float表示价格
 */
public class FloatShoppingDemo {
    public static void main(String[] args) {
        // 商品信息
        float price = 188.88f;       // 单价
        float discount = 88.8f;      // 折扣价
        float shippingFee = 8.8f;    // 运费
        int quantity = 2;            // 购买数量(int)
        
        // 计算总金额
        float subtotal = price * quantity;
        float totalAmount = subtotal + shippingFee;
        
        // 余额
        float accountBalance = 6666.66f;
        float afterPayment = accountBalance - totalAmount;
        
        System.out.println("【飞翔商城 - 购物结算】");
        System.out.println("单价:¥" + price);
        System.out.println("数量:" + quantity + "件");
        System.out.println("小计:¥" + subtotal);
        System.out.println("运费:¥" + shippingFee);
        System.out.println("实付:¥" + totalAmount);
        System.out.println("账户余额(支付前):¥" + accountBalance);
        System.out.println("账户余额(支付后):¥" + afterPayment);
        
        System.out.println("\n【精度观察】");
        System.out.println("188.88 × 2 = " + (188.88f * 2));  // 结果可能不是精确的377.76
    }
}
运行输出:
【飞翔商城 - 购物结算】
单价:¥188.88
数量:2件
小计:¥377.76
运费:¥8.8
实付:¥386.56
账户余额(支付前):¥6666.66
账户余额(支付后):¥6280.1006

【精度观察】
188.88 × 2 = 377.76

注意:6280.1006并非精确值,而是二进制近似值的最接近十进制表示。这就是float的精度限制。

示例二:float的特殊值

/**
 * 飞翔科技 - float 特殊值演示
 * 场景:演示无穷大、NaN、正负零等特殊值
 */
public class FloatSpecialDemo {
    public static void main(String[] args) {
        // 无穷大
        float positiveInfinity = 1.0f / 0.0f;
        float negativeInfinity = -1.0f / 0.0f;
        
        // NaN(Not a Number)
        float nan1 = 0.0f / 0.0f;
        float nan2 = (float) Math.sqrt(-1.0);
        
        // 正负零
        float positiveZero = 0.0f;
        float negativeZero = -0.0f;
        
        // Float 常量
        System.out.println("【float 特殊值】");
        System.out.println("正无穷:" + positiveInfinity + " (等于Float.POSITIVE_INFINITY:" 
                           + (positiveInfinity == Float.POSITIVE_INFINITY) + ")");
        System.out.println("负无穷:" + negativeInfinity);
        System.out.println("NaN(0/0):" + nan1);
        System.out.println("NaN(√-1):" + nan2);
        System.out.println("NaN == NaN:" + (nan1 == nan2));  // NaN不等于任何值,包括自己!
        System.out.println("NaN isNaN:" + Float.isNaN(nan1));
        
        System.out.println("\n正零 == 负零:" + (positiveZero == negativeZero));  // true
        System.out.println("1.0/正零 = " + (1.0f / positiveZero));  // Infinity
        System.out.println("1.0/负零 = " + (1.0f / negativeZero));  // -Infinity
        
        System.out.println("\n【float 范围常量】");
        System.out.println("最小值(正数): " + Float.MIN_VALUE);     // 1.4E-45
        System.out.println("最大值: " + Float.MAX_VALUE);            // 3.4028235E38
        System.out.println("最小规格化正数: " + Float.MIN_NORMAL);    // 1.17549435E-38
    }
}
运行输出:
【float 特殊值】
正无穷:Infinity (等于Float.POSITIVE_INFINITY:true)
负无穷:-Infinity
NaN(0/0):NaN
NaN(√-1):NaN
NaN == NaN:false
NaN isNaN:true

正零 == 负零:true
1.0/正零 = Infinity
1.0/负零 = -Infinity

【float 范围常量】
最小值(正数): 1.4E-45
最大值: 3.4028235E38
最小规格化正数: 1.17549435E-38

易错场景

反例一:忘记f后缀——float最经典的编译错误

// ❌ 错误:浮点字面量默认是 double,不能直接赋给 float
float price = 188.88;  // 编译错误:incompatible types: possible lossy conversion from double to float

// ✅ 正确:加 f/F 后缀
float price = 188.88f;
float price2 = 188.88F;  // 大写也可以

反例二:用float做精确的金额计算

// ❌ 危险:float 累加会累积精度误差
float total = 0.0f;
for (int i = 0; i < 1000; i++) {
    total += 0.1f;  // 期望100.0,实际是 99.99905 左右
}
System.out.println(total);  // 输出 99.99905(不是精确的100)

// ✅ 正确:金融计算使用 BigDecimal
import java.math.BigDecimal;
BigDecimal exactTotal = BigDecimal.ZERO;
for (int i = 0; i < 1000; i++) {
    exactTotal = exactTotal.add(new BigDecimal("0.1"));
}
System.out.println(exactTotal);  // 输出 100.0(精确)

反例三:直接用==比较float

// ❌ 错误:直接 == 比较浮点数
float a = 0.1f + 0.2f;
float b = 0.3f;
System.out.println(a == b);  // 输出 false!(期望true)

// ✅ 正确:用误差范围比较
float epsilon = 0.000001f;
boolean isEqual = Math.abs(a - b) < epsilon;
System.out.println(isEqual);  // 输出 true

适用场景与选择指南

场景类型选择原因
商品价格/工资(演示)float教学演示,实际项目用BigDecimal
气温/体温float精度要求不高
3D游戏坐标floatGPU运算默认float,省内存
传感器数据float原始精度有限
科学计算double需要更高精度
金融计算BigDecimal需要精确十进制

面试考点

Q1:float的f后缀为什么不能省略?

Java中浮点数字面量(如3.14)默认是double类型(64位)。double→float是窄化转换(narrowing),可能有精度损失,编译器不允许隐式进行。必须显式加f后缀告诉编译器"我知道这是float"。

Q2:float f = 1.0f / 0.0f; 会抛异常吗?

不会。IEEE 754标准定义了浮点除零的行为:正数÷0 = Infinity,负数÷0 = -Infinity,0÷0 = NaN。这与整数除零(会抛出ArithmeticException)完全不同。因此浮点运算不需要try-catch除零异常。

Q3:为什么0.1f + 0.2f ≠ 0.3f?

0.1和0.2在二进制中都是无限循环小数,float只能存储23位尾数的近似值。两个近似值相加得到的近似值,与0.3的近似值不相等。这不是Java的bug,而是所有遵循IEEE 754标准的语言(C、Python、JavaScript等)都有的现象。

Q4:float和double在JVM的原子性方面有什么区别?

与long类似,32位JVM上非volatile的float和double的64位读写不保证原子性(float是32位,通常原子;double是64位,不保证)。但在64位JVM的HotSpot实现中,对float的读写通常是原子的。规范建议:多线程共享的double变量应声明为volatile或使用AtomicLong的Double.longBitsToDouble方式。

上一页
长整型 long
下一页
双精度浮点型 double