【问题标题】:Can I trust sprintf() to eliminate floating point imprecision?我可以相信 sprintf() 来消除浮点不精确吗?
【发布时间】:2022-01-09 11:38:48
【问题描述】:

我有以下 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 ^(大多数小数位)。处理的数字是用户输入的,属于线性方程组。在大多数情况下,两位小数就足够了。

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

【问题讨论】:

  • “Floating point imprecision”并不是你想的那样,而且这个问题中没有足够的信息来说明结果是否会是你想要的。
  • 当我用1.4 调用函数时,我希望得到1 返回。我不想让我的程序将号码处理为1.3999999999999...
  • 是的,我不确定'消除浮点不精确'实际上是否可行,在一般情况下,不牺牲准确性:(
  • 你是对的,但到目前为止,sprintf()printf() 可以告诉我返回我想要看到的值而不是内部表示。所以我想知道我是否可以相信它来确定数字的小数位。
  • 显示浮点数与实际显示的浮点数不同。

标签: c printf precision


【解决方案1】:

您的函数只能为任何有限数生成 6 位小数,因此它不能返回大于 6 的小数位数,并且返回的小数位数可能比预期的要小得多:如果该值不是整数但足够接近,该函数将返回0,例如:getDecimalPlaces(1.0000004)getDecimalPlaces(1.9999996)

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

如果您想确定明确表示值n 所需的小数位数,则应使用%320.18g 并处理可能为-11 之间的值生成的指数部分。

但是请注意,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] = '\0';
    if (len > 0 && string[len - 1] == '.')
        string[--len] = '\0';
    return len;
}

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

【讨论】:

  • 对不起,如果我可能误解了这一点,但是当我指定任何双精度值时,函数 在所有情况下都返回了正确的数字。 double d = 3.451; int k = getDecimalPlaces(d); printf("%d\n", k);--&gt; 3
  • @Luqus:你是对的,但函数可以返回 0 的值不是整数,例如 0.0000001
  • Re “你的函数应该为任何有限数返回 6”:getDecimalPlaces(3.5)) 返回 1。
  • @EricPostpischil:你是对的,我修改了答案。
  • @chqrlie 我知道你的意思。对于太小的数字,这不是一个完美的功能,我可以对其进行编辑,但对于我的用例来说不是必需的。
猜你喜欢
  • 1970-01-01
  • 2010-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-14
  • 1970-01-01
  • 2021-12-06
相关资源
最近更新 更多