关于浮点数的思考

在计算机中,十进制的小数是无法直接精确表示的,因此需要将其转换为二进制表示。二进制小数表示与十进制小数的表示方法相似,但是它的基数是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 提供了任意精度的数值表示,避免了浮点数表示的舍入误差。

相关推荐
m0_471199637 分钟前
【小程序】订单数据缓存 以及针对海量库存数据的 懒加载+数据分片 的具体实现方式
前端·vue.js·小程序
编程大师哥8 分钟前
Java web
java·开发语言·前端
A小码哥9 分钟前
Vibe Coding 提示词优化的四个实战策略
前端
Murrays10 分钟前
【React】01 初识 React
前端·javascript·react.js
大喜xi13 分钟前
ReactNative 使用百分比宽度时,aspectRatio 在某些情况下无法正确推断出高度,导致图片高度为 0,从而无法显示
前端
helloCat13 分钟前
你的前端代码应该怎么写
前端·javascript·架构
电商API_1800790524714 分钟前
大麦网API实战指南:关键字搜索与详情数据获取全解析
java·大数据·前端·人工智能·spring·网络爬虫
康一夏15 分钟前
CSS盒模型(Box Model) 原理
前端·css
web前端12315 分钟前
React Hooks 介绍与实践要点
前端·react.js
我是小疯子6616 分钟前
JavaScriptWebAPI核心操作全解析
前端