江小南

V1

2023/01/29阅读:34主题:萌绿

【C语言】IEEE754标准与浮点型精度丢失

阅读本文,需要对C语言的基础知识有所了解。C语言基础,我给大家准备了详细的学习资料,微信公众号主页回复“C语言”即可领取。

同时,你应当阅读了小端存储与位运算的知识:

小端存储与补码

位运算符

1. IEEE754标准

在C语言中,float型变量占用的内存空间为4字节,double型变量占用的内存空间为8字节。

与整型数据的存储方式不同,浮点型数据是按照指数形式存储的。在IEEE754标准中,系统把一个浮点型数据分成小数部分(用M表示)和指数部分(用E表示)并分别存放,指数部分采用规范化的指数形式,指数也分为正、负(符号位,用S表示)。

数符(即符号位)占1位,是0时代表正数,是1时代表负数。

示例

#include <stdio.h>

int main() {
    float f=4.5;
    float f1=1.456;
    return 0;
}

可以看到,4.5的内存是0x40900000。1.456的内存为0x355eba3f。

解析:我们使用IEEE754标准来表示4.5

格式 SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM
二进制 0100 0000 1001 0000 0000 0000 0000 0000
十六进制 40 90 00 00

即:

S(符号位) E(阶码) M(尾数)
0 1000 0001 0010 0000 0000 0000 0000 000
  • S:S是符号位,用来表示正、负,是1表示负数,是0表示正数。
  • E:E代表指数部分(指数部分的值规定只能是1到254,不能全是0或全是1),指数部分运算前都要减去127,因为还要表示负指数。这里的10000001转换为十进制数为129,129-127=2,即实际指数部分为2。
  • M:M代表小数部分,这里为0010 0000 0000 0000 0000 000,底数左边省略存储了一个1,使用的实际底数表示为1.00100000000000000000000。

首先看f的小数部分。这里为0010 0000 0000 0000 0000 000,总计23位。由于底数左边省略存储了一个1,所以实际底数部分表示为1.00100000000000000000000。

再看指数部分,计算机并不能直接计算10的幂次,f的指数部分为1000 0001,其十进制值为129,129-127=2,即实际指数部分为2,指数值为2,代表2的2次幂。因此将1.001左移2位即可,也就是100.1,然后转换成十进制数,整数部分为4,小数部分为2的-1次方,为0.5,因此十进制数为4.5。

1.456使用同样的方法可以得出。

结论

2. 浮点型精度丢失

浮点型变量分为单精度(float)型,双精度(double)型。

浮点型使用的是指数表示法,需要记忆数值范围。

表中的double类型是-1022到1023,是通过1-2046(不能全是0或全是1,全1是2047)减去1023,得到-1022到1023。

示例

#include <stdio.h>

// 提醒:scanf读取double类型时,要用lf,如double d;scanf("%lf",&d);
int main() {
    float a=1.23456789e10;
    float b;
    b=a+20;  // 计算时精度丢失
    double c=1.23456789e10;
    double d;
    d=c+20;
    printf("b=%f\n",b); // %f既可以输出float,也可以输出double类型
    printf("d=%f\n",d);
    return 0;
}
F:\Computer\Project\practice\20\20.6-double\cmake-build-debug\20_6_double.exe
b=12345678848.000000
d=12345678920.000000

进程已结束,退出代码为 0

分析:对于程序中,我们赋给a的值为1.23456789e10,加20后,应该得到的值是1.234567892e10,但b输出的结果却是b=12345678848.000000,变得更小了。我们将这种现象称为精度丢失,因为float类型数据能够表示的有效数字为7位,最多只保证1.234567e10的正确性。要使结果正确,就需要把a和b均改为double类型,因为double可以表示的精度为15-16位

注意:对于强制类型转换,int转float可能造成精度丢失,因为int是有10位有效数字的,但是int强制转为double不会,float转为double也不会丢失精度。

分类:

后端

标签:

C++

作者介绍

江小南
V1