我可以相信 sprintf() 可以消除浮点不精确吗?

Can I trust sprintf() to eliminate floating point imprecision?

我有以下 C 代码来确定双精度值的小数位。它使用 sprintf()%f 来结束一个没有浮点不精确的字符串。是否存在 sprintf() 没有正确消除浮点不精确的风险,或者该函数是否可以安全使用?

#include <stdio.h>
#include <stdbool.h>

#define MAX_STRING 256

int getDecimalPlaces(double n)
{
    char string[MAX_STRING];
    int numberOfDecimalPlaces = 0;
    int i = sprintf(string, "%f", n);
    bool countZero = false;
    for (int j = i - 1; j > 0; j--)
    {
        if (string[j] == '.')
            break;
        if (!countZero && string[j] != 48)
            countZero = true;
        if (countZero)
            numberOfDecimalPlaces++;
    }
    return numberOfDecimalPlaces;
}

我调用数字函数来对它们执行数学运算(最不常见的乘法)。我需要此操作的整数,以便我搜索小数位数最多的数字。然后我将每个数字乘以 10 ^(大多数小数位)。处理的数字是用户输入的,属于线性方程组。在大多数情况下,2 位小数就足够了。

数字本身不必 非常 精确,但它们的小​​数位 与用户输入的内容相匹配.

您的函数只为任何有限数生成 6 位小数,因此它不能 return 大于 6 的小数位数,returned 的小数位数可以小得多超出预期:如果值不是整数但足够接近,函数将 return 0,例如:getDecimalPlaces(1.0000004)getDecimalPlaces(1.9999996).

%f 将数字格式化为固定小数位数,您可以使用 precision 字段指定小数位数,例如 %.10f。默认为 6.

如果要明确表示值 n 所需的小数位数,应使用 %320.18g 并处理可能为 [=20= 之间的值生成的指数部分] 和 1.

但是请注意,double 类型在内部使用二进制表示浮点值。除非该值是 2 的负幂的精确倍数,否则表示形式是其十进制表示形式的近似值。

例如1.5是精确表示,没有近似值,但是0.1不是,就像1/3不能用小数表示一样。

0.1的内部表示是近似值,但是根据sprintf()的实现,将这个近似值转换为十进制可以产生0.1000000000000000000013552527156068805425093160010874271392822265625或者只是0.1,因为两者都会转换回相同的值。

sprintf 在您的函数中使用编码不安全:对于大于 1e250 的非常大的数字,snprintf 将产生超过 256 个字符并写入数组末尾.使用 snprinf.

更安全

如果您的目标是将值转换为最多 6 位小数且没有指数的十进制形式的值,您可以更改您可以使用此修改版本:

#include <stdio.h>

int convertValue(char *dest, size_t size, double d, int maxplaces) {
    int len = snprintf(dest, "%.*f", maxplaces, d);

    while (n > 0 && string[len - 1] == '0')
        string[--len] = '[=10=]';
    if (len > 0 && string[len - 1] == '.')
        string[--len] = '[=10=]';
    return len;
}

编辑: 为了您的目的,您想将数字转换为整数。确保使用 round(d * power_of_10) 以确保正确转换。如果没有 round(),您可能会得到不正确的转换,例如 0.7 * 10 转换为 6 而不是 7