当浮点数是函数的实参时,为什么我的计算机会随意更改浮点数末尾的十进制数字?

Xet*_*t46 2 c floating-point

我试图满足这个问题:编写一个函数 print_dig_float(float f) 来打印浮点数 f 的每个数字的值。例如,如果 f 是 2345.1234,则 print_dig_float(f) 将连续打印数字 2、3、4、5、1、2、3 和 4 的整数值。

我所做的是:给定一个带小数的数字,我尝试将数字向左移动(例如:3.45 -> 345),将其乘以 10。之后,我通过取余数将每个数字存储在一个数组中,然后把它放在一个元素中。然后,我将它们打印出来。

所以我的程序是这样的:

#include <stdio.h>

void print_dig_float(float f);

int main(int argc, char const *argv[]) {

    print_dig_float(23432.214122);

    return 0;
}

void print_dig_float(float f) {

    printf("%f\n", f);

    int i = 0, arr[50], conv;

    //move digits to the left

    do {

        f = f * 10;
        conv = f;
        printf("%i\n", conv);

    } while (conv % 10 != 0);

    conv = f / 10;

    //store digits in an array

    while (conv > 1) {
        arr[i] = conv % 10;
        conv = conv / 10;
        i++;
    }

    for (int j = i - 1; j >= 0; j--) {
        printf("%i ", arr[j]);
    }

    printf("\n");

}
Run Code Online (Sandbox Code Playgroud)

当我用数字测试它时:23432.214122,这就是我得到的(根据Linux终端):

23432.214844

234322

2343221

23432216

234322160

2 3 4 3 2 2 1 6

问题是,正如您在上面看到的,计算机甚至在我对它进行任何操作之前就任意更改了数字末尾的十进制数字。不知道是我的问题还是电脑的问题导致这个问题。

Eri*_*hil 6

根据 C 2018 5.2.4.2.2,浮点数用符号、某个指数的固定基数和由该基数中的数字组成的数字表示。最常见的是,使用两个作为基础。

当基数为 2 时,23432.214122 不能用浮点数表示,因为每个可表示的数都必须是基数的幂的整数倍(可能是负幂)。23432.214122 不是 1/2、1/ 4、?、1/2 4、1/2 5或任何其他 2 的幂的倍数。

23432.214122在源代码时,它被转换成可表示的值。在良好的 C 实现中,使用最接近的可表示值,但 C 标准允许使用最接近的较大值或最接近的较小值的可表示值。除此之外,出现的数字不是任意的;它们是数学的结果。

当 IEEE-754 binary32 用于 时float,最接近 23432.214122 的可表示值正好是 23432.21484375。

因为,当 C 实现对浮点数使用基数 2 时,浮点数具有二进制数字而没有十进制数字。尝试从没有十进制数字的事物中提取十进制数字通常没有意义。在受浮点格式影响的某些限制内,可以确定原始数字中的十进制数字。但是,“23432.214122”的位数太多,无法使用 32 位浮点类型执行此操作。使用 64 位类型,通常用于double,您可以恢复原始数字,前提是您知道有多少个十进制数字开始。如果没有这些信息,通常不可能恢复原始数字 - 正如您所看到的,尾随数字会有所不同,并且浮点数本身没有指示差异从哪里开始。