关于浮点数的思考

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

相关推荐
gnip6 小时前
企业级配置式表单组件封装
前端·javascript·vue.js
一只叫煤球的猫7 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
excel8 小时前
Three.js 材质(Material)详解 —— 区别、原理、场景与示例
前端
掘金安东尼9 小时前
抛弃自定义模态框:原生Dialog的实力
前端·javascript·github
hj5914_前端新手12 小时前
javascript基础- 函数中 this 指向、call、apply、bind
前端·javascript
薛定谔的算法12 小时前
低代码编辑器项目设计与实现:以JSON为核心的数据驱动架构
前端·react.js·前端框架
Hilaku13 小时前
都2025年了,我们还有必要为了兼容性,去写那么多polyfill吗?
前端·javascript·css
yangcode13 小时前
iOS 苹果内购 Storekit 2
前端
LuckySusu13 小时前
【js篇】JavaScript 原型修改 vs 重写:深入理解 constructor的指向问题
前端·javascript
LuckySusu13 小时前
【js篇】如何准确获取对象自身的属性?hasOwnProperty深度解析
前端·javascript