c - 当浮点数是函数的实际参数时,为什么我的计算机会任意更改浮点数末尾的小数位?
问题描述
我试图解决这个问题:编写一个函数 print_dig_float(float f),它打印浮点数 f 的每个数字的值。例如,如果 f 是 2345.1234,print_dig_float(f) 将连续打印数字 2、3、4、5、1、2、3 和 4 的整数值。
我所做的是:给定一个带有一些小数的数字,我尝试通过将其乘以 10 将数字向左移动(例如:3.45 -> 345)。之后,我通过取余数将每个数字存储在一个数组中并把它放在一个元素中。然后,我将它们打印出来。
所以我的程序是这样的:
#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");
}
当我用数字测试它时:23432.214122,这就是我得到的(根据Linux终端):
23432.214844
234322
2343221
23432216
234322160
2 3 4 3 2 2 1 6
问题是,正如您在上面看到的,即使在我对它做任何事情之前,计算机也会任意更改数字末尾的十进制数字。我不知道这是我的错还是电脑的问题。
解决方案
根据 C 2018 5.2.4.2.2,浮点数用符号、某个指数的固定基数以及由该基数中的数字组成的数字表示。最常见的是,两个用作基础。
当基数为 2 时,23432.214122 不能用浮点数表示,因为每个可表示的数字都必须是基数的某个整数倍数(可能是负数)。23432.214122 不是 ½、¼、⅛、1/2 4、1/2 5或任何其他 2 的幂的倍数。
在23432.214122
源代码中使用时,将其转换为可表示的值。在良好的 C 实现中,使用最接近的可表示值,但 C 标准允许使用最接近的较大或最接近的较小值的可表示值。除此之外,出现的数字不是任意的;它们是数学的结果。
当 IEEE-754 binary32 用于 时float
,最接近 23432.214122 的可表示值正好是 23432.21484375。
因为,当 C 实现使用以二为底的浮点数时,浮点数具有二进制数字而没有十进制数字。尝试从没有十进制数字的事物中提取十进制数字通常是没有意义的。可以确定原始数字中的十进制数字,直至受到浮点格式影响的某个限制。但是,“23432.214122”的位数太多,无法使用 32 位浮点类型执行此操作。使用 64 位类型,通常用于double
,您可以恢复原始数字,前提是您知道有多少个十进制数字开头。如果没有这些信息,通常不可能恢复原始数字 - 正如您所看到的尾随数字会不同,并且浮点数本身没有指示差异开始的位置。
推荐阅读
- window - Drools 窗口长度和行为
- c - 将字符串初始化为 {0, } 有什么作用?
- python - 这是一个异步问题吗?
- typescript - 定义索引类型时,可以限制存在的属性数量吗?
- syntax - 如何解决这种转变/减少冲突?
- python - Django import export 在导出前编辑查询集
- java - 尝试使用具有不一致数据类型的 REST API 作为响应
- css - 有没有办法使用 css 操作 SVG 的“内部笔画”?
- sql - 使用 ts-postgres 将多少行插入 SQL 数据库?
- postgresql - Golang 使用 SSL 证书连接到 Postgres