关于浮点数的思考

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

相关推荐
多啦爱梦的梦想38 分钟前
项目中把webpack 打包改为vite 打包
前端·webpack
小刘不知道叫啥1 小时前
简单说一下 Webpack分包
前端·javascript·webpack·node.js
Dontla1 小时前
《黑马前端ajax+node.js+webpack+git教程》(笔记)——node.js教程+webpack教程(nodejs教程)
前端·ajax·node.js
繁依Fanyi1 小时前
项目记录:「五秒反应挑战」小游戏的开发全过程
前端·codebuddy首席试玩官
肥肠可耐的西西公主2 小时前
前端(vue)学习笔记(CLASS 6):路由进阶
前端·vue.js·学习
*小雪2 小时前
uniapp打包H5,输入网址空白情况
前端·uni-app
李梨与狸2 小时前
vue中excel文件 打包后不展示问题
前端·vue.js·excel
前端达人2 小时前
React 播客专栏 Vol.13|样式不难搞,Tailwind CSS 与 SVG 实战入门
前端·javascript·css·react.js·前端框架
xcs194052 小时前
开发 前端搭建npm v11.4.0 is known not to run on Node.js v14.18.1.
前端·npm·node.js
等等5432 小时前
CSS高级技巧
前端·css