关于c:C-语言-float-内存布局详解

51次阅读

共计 2537 个字符,预计需要花费 7 分钟才能阅读完成。

前言

C 语言中的 float 并不像大多数人设想的那样, 因为计算机模仿的起因, 其本质是离散的而非间断的, 所以精度和范畴是肯定的, 这些都写在 float.h 头文件的宏中.

但通常, 咱们对教材的每一个字都意识, 连起来就读不懂了, 所以, 写下此博文, 详解之.

学过深刻了解计算机系统的同学, 都晓得 float 的实现形式, 依照 IEEE 规范, 由符号位, 阶码位, 尾数位组成, 本文给出一个代码, 打印 float 的符号位, 阶码位, 尾数位.


一、float 中的宏定义

这是 float.h 中无关 float 具体实现, 范畴, 精度等的宏常量, 根据 64 位零碎环境, 咱们一一解读.

#include <float.h>
    FLT_RADIX;      // 2;
    FLT_MANT_DIG;   // 24;
    FLT_DIG;        // 6;
    FLT_MIN_10_EXP; // (-37);
    FLT_MAX_10_EXP; // 38;
    FLT_EPSILON;    // 1.19209290e-7F;
    FLT_MAX;        // 3.40282347e+38F;
    FLT_MIN;        // 1.17549435e-38F;

FLT_RADIX 2

This is the value of the base, or radix, of the exponent representation. This is guaranteed to be a constant expression, unlike the other macros described in this section. The value is 2 on all machines we know of except the IBM 360 and derivatives.

这是指数示意的基数或基数的值。这保障是一个常量表达式,与本节中形容的其余宏不同。在咱们所知的所有机器上,该值均为 2,除了 IBM 360 及其衍生产品。

float 基于二进制实现, 其基数是 2.

换句人话说, 所有 float 浮点数, 都是由一系列 2 的 n 次方相加组成.

比方 0.5 就是
2^-1 次方,

0.25 就是
2^-2 次方,

0.75 就是
2^-1 + 2^-2

FLT_MANT_DIG 24

This is the number of base-FLT_RADIX digits in the floating point mantissa for the float data type. The following expression yields 1.0 (even though mathematically it should not) due to the limited number of mantissa digits:

这是浮点数据类型的浮点尾数中的基数 FLT_RADIX 位数。因为尾数位数无限,以下表达式产生 1.0(只管在数学上不应该产生):

float radix = FLT_RADIX; // 2.0

1.0f + 1.0f / radix / radix / … / radix; // 在 float 中, 1.0 其实是 1.0 + 1.0 间断除以 24 个 2.0

where radix appears FLT_MANT_DIG times.

radix 呈现了 FLT_MANT_DIG 次, 也就是 24 次.

这个细节如果有人看过深刻了解操作系统, 那么就应该明确了, 在 IEEE 规范中, float 的尾数是 23 位, 但因为通常都是 1.0+0.N 的模式, 所以相当于第一位是隐式的 1, 加 23 位就是 24 位.

这 24 位就是 float 的二进制精度.

FLT_DIG 6

float 的十进制精度, 包含整数局部及小数局部.

比方 123.456 这就是 6 位精度, 再多就不保障精度可用了, 比方 123.4567, 其中 7 就不保障是正确的.

FLT_MIN_10_EXP (-37)

这代表 float 能保障的最小的 10 进制指数, 我这里是 10^-37 次方, 再小就不保障正确了.

FLT_MAX_10_EXP 38

这是 float 能保障的最大的 10 进制指数, 再大就不保障正确了

FLT_EPSILON 1.19209290e-7F

这个不好了解, 它的意思是 1.0 和比 1.0 大的最小的 float 值的差.

FLT_MAX 3.40282347e+38F

float 能示意的最大数.

FLT_MIN 1.17549435e-38F

float 能示意的最小数.

二、float 内存布局打印实现算法

根本思维是实现一个位域构造, 将一个 32 位的整数分成三份, 一份占 1 位, 批示符号, 一份占 8 位, 批示阶码, 一份占 23 位, 批示尾数.

typedef struct
{
    uint32_t Mantissa : 23;
    uint32_t Exponent : 8;
    uint32_t Sign : 1;
} fltToBit;

因为是小端序, 逆着排, 也就是尾数, 阶码, 符号.

通过 itoa() 函数, 将整数转为二进制字符串, 并进行打印.

二、float 内存布局打印实现代码

代码比拟容易, 惟一不好了解的是:

    fltToBit test = *(fltToBit *)&a;

float 不能间接强制转为 fltToBit, 须要先取地址, 强转为 fltToBit 指针, 再解援用.

其余的代码都应该能懂.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    uint32_t Mantissa : 23;
    uint32_t Exponent : 8;
    uint32_t Sign : 1;
} fltToBit;

void print_float_as_binary(float a)
{char BufExponet[32] = "";
    char BufMantissa[32] = "";

    fltToBit test = *(fltToBit *)&a;

    printf("Sign: %u\nExponent: %08s\nMantissa: %023s\n", test.Sign,
           itoa(test.Exponent, BufExponet, 2),
           itoa(test.Mantissa, BufMantissa, 2));
}

int main()
{
    const float a = 1.4142136F;
    print_float_as_binary(a);
    return 0;
}

总结

float 是用离散办法实现无限精度的浮点数, 有工夫应认真查阅文档进行斟酌, 很有意思.

本文介绍了 IEEE 对 float 的实现, 利用数据结构, 将其外部实在二进制值打印进去, 不便读者钻研。

正文完
 0