关于浮点数的思考

在计算机中,十进制的小数是无法直接精确表示的,因此需要将其转换为二进制表示。二进制小数表示与十进制小数的表示方法相似,但是它的基数是2,而不是10。下面是将十进制小数转换为二进制小数的过程:

1. 整数部分的二进制表示

整数部分的二进制表示是通过不断地将该整数除以2,记录下每次的余数,直到商为0。

例如,十进制数 13 的二进制表示:

java 复制代码
13 ÷ 2 = 6 余 1
6 ÷ 2 = 3 余 0
3 ÷ 2 = 1 余 1
1 ÷ 2 = 0 余 1

所以,13 的二进制表示是 1101

2. 小数部分的二进制表示

小数部分的二进制表示通过不断地将小数部分乘以2,记录下每次的整数部分(乘积的整数部分),直到小数部分为0或者达到精度限制。

例如,十进制小数 0.625 的二进制表示:

java 复制代码
0.625 × 2 = 1.25 → 取整数部分 1
0.25 × 2 = 0.5 → 取整数部分 0
0.5 × 2 = 1.0 → 取整数部分 1

所以,0.625 的二进制表示是 0.101

3. 组合整数部分和小数部分

将整数部分和小数部分结合起来,得到最终的二进制表示。

例如,十进制数 13.625

  • 整数部分 13 的二进制是 1101
  • 小数部分 0.625 的二进制是 0.101

所以,13.625 的二进制表示是:1101.101

4. 浮点数表示问题

有些十进制的小数在二进制中无法精确表示。例如,十进制的 0.1 在二进制中是一个无限循环小数:

java 复制代码
0.1 × 2 = 0.2 → 取整数部分 0
0.2 × 2 = 0.4 → 取整数部分 0
0.4 × 2 = 0.8 → 取整数部分 0
0.8 × 2 = 1.6 → 取整数部分 1
0.6 × 2 = 1.2 → 取整数部分 1
0.2 × 2 = 0.4 → 取整数部分 0 (开始循环)

这个过程会一直循环下去,因此 0.1 的二进制表示是一个无限循环小数,无法完全精确表示。这是浮点数表示误差的一个常见原因。

总结:

  • 整数部分:通过除以2取余来获得。
  • 小数部分:通过乘以2并取整数部分来获得。
  • 无法精确表示的十进制小数 :例如 0.10.3,它们在二进制中会变成无限循环小数,导致无法精确表示。

这也是为什么在计算机中使用浮点数时,可能会出现一些精度误差的原因,特别是在处理类似 0.1 这种无法精确表示的数字时。

java int long float double范围与精度是怎么样的?

在 Java 中,不同的数值类型(intlongfloatdouble)具有不同的范围和精度。下面是每个类型的详细信息:

1. int 类型

  • 字节数:4 字节(32 位)
  • 范围-2,147,483,6482,147,483,647(即 -2^312^31 - 1
  • 精度int 是一个整数类型,表示没有小数部分的整数,因此它的精度是准确的,但它只能表示整数范围内的值。

示例:

java 复制代码
int a = 2147483647;  // 最大值
int b = -2147483648; // 最小值

2. long 类型

  • 字节数:8 字节(64 位)
  • 范围-9,223,372,036,854,775,8089,223,372,036,854,775,807(即 -2^632^63 - 1
  • 精度long 是一个整数类型,表示没有小数部分的整数,因此它的精度也是准确的,但它有更大的范围,比 int 能够表示更大的整数。

示例:

java 复制代码
long a = 9223372036854775807L;  // 最大值
long b = -9223372036854775808L; // 最小值

注意:long 类型的值需要在字面量后加上 L 来区分(例如 9223372036854775807L)。

3. float 类型

  • 字节数:4 字节(32 位)
  • 范围 :约 1.4 × 10^−453.4 × 10^38(正负数)
  • 精度float 类型使用 23 位尾数和 8 位指数(符合 IEEE 754 单精度浮点数标准),它的有效数字精度大约是 6-7 位十进制数字 。因此,float 适用于不需要非常高精度的场景,但有可能会产生舍入误差,特别是在进行精确计算时。

示例:

java 复制代码
float a = 3.14f;  // 需要加上 'f' 来表示浮点数

4. double 类型

  • 字节数:8 字节(64 位)
  • 范围 :约 4.9 × 10^−3241.8 × 10^308(正负数)
  • 精度double 类型使用 52 位尾数和 11 位指数(符合 IEEE 754 双精度浮点数标准),它的有效数字精度大约是 15-16 位十进制数字double 提供比 float 更高的精度,适用于对精度要求较高的计算。

示例:

java 复制代码
double a = 3.141592653589793;  // 可以表示更高精度的小数

总结:范围与精度

数据类型 字节数 范围 精度
int 4 -2,147,483,6482,147,483,647 精确整数,32 位整数
long 8 -9,223,372,036,854,775,8089,223,372,036,854,775,807 精确整数,64 位整数
float 4 1.4 × 10^−453.4 × 10^38(正负数) 有效数字约 6-7 位十进制数字
double 8 4.9 × 10^−3241.8 × 10^308(正负数) 有效数字约 15-16 位十进制数字

500000.05f为什么无法被float精准表示?

在 Java 中,500000.05f 无法被精确表示为 float 类型的原因在于 浮点数的二进制表示 限制。float 类型采用的是 IEEE 754 单精度浮点数标准 ,其尾数部分只有 23 位(加上 1 位隐含的 1),而指数部分有 8 位,因此它无法精确表示某些十进制小数,尤其是像 500000.05f 这样的数字。

float 的二进制表示

float 类型使用 32 位来表示浮点数,分为三个部分:

  1. 符号位:1 位,用于表示数字的正负。
  2. 指数位:8 位,用于表示指数部分。
  3. 尾数位 :23 位(加上 1 位隐含的 1),表示数字的精度部分。

计算过程:将 500000.05f 转换为二进制

  1. 整数部分 500000 的转换 : 将整数 500000 转换为二进制。

    yaml 复制代码
    500000 ÷ 2 = 250000 余 0
    250000 ÷ 2 = 125000 余 0
    125000 ÷ 2 = 62500 余 0
    62500 ÷ 2 = 31250 余 0
    31250 ÷ 2 = 15625 余 0
    15625 ÷ 2 = 7812 余 1
    7812 ÷ 2 = 3906 余 0
    3906 ÷ 2 = 1953 余 0
    1953 ÷ 2 = 976 余 1
    976 ÷ 2 = 488 余 0
    488 ÷ 2 = 244 余 0
    244 ÷ 2 = 122 余 0
    122 ÷ 2 = 61 余 0
    61 ÷ 2 = 30 余 1
    30 ÷ 2 = 15 余 0
    15 ÷ 2 = 7 余 1
    7 ÷ 2 = 3 余 1
    3 ÷ 2 = 1 余 1
    1 ÷ 2 = 0 余 1

    结果:500000 的二进制表示是:11110100001001000000

  2. 小数部分 0.05 的转换 : 将小数部分 0.05 转换为二进制:

    java 复制代码
    0.05 × 2 = 0.10 → 取整数部分 0
    0.10 × 2 = 0.20 → 取整数部分 0
    0.20 × 2 = 0.40 → 取整数部分 0
    0.40 × 2 = 0.80 → 取整数部分 0
    0.80 × 2 = 1.60 → 取整数部分 1
    0.60 × 2 = 1.20 → 取整数部分 1
    0.20 × 2 = 0.40 → 取整数部分 0 (开始循环)

    结果:0.05 的二进制表示是 0.00001100110011...(循环小数)。

  3. 合并整数部分和小数部分 : 所以,500000.05 的二进制表示为:

    java 复制代码
    500000.05 → 11110100001001000000.00001100110011...

500000.05 表示为 float

float 类型中,精度有限,最多只有 23 位有效数字。因此,它不能精确表示 500000.05 的完整二进制表示。

  1. 整数部分500000 的二进制表示为 11110100001001000000,它可以完全表示在 float 类型中。
  2. 小数部分0.05 的二进制表示是一个循环小数:00001100110011...。由于 float 类型只有 23 位尾数,不能精确表示这个无限循环的小数,因此它会被舍入。

结果:

由于 float 的精度限制,500000.05f 会被舍入到最接近的能够表示的二进制值。这个值可能不是精确的 500000.05,而是一个近似值,这就是为什么你在 Java 中使用 float 时,像 500000.05f 这样的数字不能精确表示的原因。

总结:

  • float 类型的尾数只有 23 位,因此对于像 500000.05f 这样的数字,它的小数部分会被舍入或截断。
  • 小数部分 0.05 在二进制中是一个无限循环小数,而 float 的精度限制导致它无法完全精确表示。
  • 为了更高的精度,建议使用 double 类型,它有更高的精度(64 位)和更广泛的有效数字位数。即使使用 double 类型,它仍然无法 完全精确表示 任何十进制小数,因为浮点数在计算机中是以二进制形式表示的,而许多十进制小数(例如 0.1, 0.05 等)在二进制中是无限循环小数。

由于计算机中的有限精度floatdouble 类型在计算机内存中的表示是有限的,因此它们只能近似地表示某些数值。double 有更多的有效位数,但它仍然有限,因此无法完全准确地表示某些十进制小数(例如 0.10.05 等)。对于精确的数值表示,尤其是在涉及金钱和财务计算时,应该使用 BigDecimal 类型,而不是浮点数类型。BigDecimal 提供了任意精度的数值表示,避免了浮点数表示的舍入误差。

相关推荐
LaoZhangAI1 小时前
Claude MCP模型上下文协议详解:AI与外部世界交互的革命性突破【2025最新指南】
前端
LaoZhangAI1 小时前
2025最全Cursor MCP实用指南:15个高效工具彻底提升AI编程体验【实战攻略】
前端
Kagerou1 小时前
vue3基础知识(结合TypeScript)
前端
市民中心的蟋蟀1 小时前
第五章 使用Context和订阅来共享组件状态
前端·javascript·react.js
逆袭的小黄鸭1 小时前
JavaScript 闭包:强大特性背后的概念、应用与内存考量
前端·javascript·面试
carterwu2 小时前
各个大厂是怎么实现组件库和相应扩展的?基础组件、区块、页面
前端
Face2 小时前
promise 规范应用
前端
Mintopia2 小时前
Node.js 中 fs.readFile API 的使用详解
前端·javascript·node.js
Face2 小时前
事件循环
前端·javascript
ONE_Gua2 小时前
chromium魔改——navigator.webdriver 检测
前端·后端·爬虫