【问题标题】:Why snprintf changes output string?为什么 snprintf 更改输出字符串?
【发布时间】:2014-08-15 14:02:22
【问题描述】:

我尝试使用 snprintf 将一些数字转换为字符串。 name1 应在逗号后与 name2 具有相同的数字。

  #include <stdio.h>
  #define length 50

  int main()
  {
  char name1 [length]; 
  char name2 [length];
  double step= 0.00001;
  unsigned long long int iterMax  =100000000000; 
  int k;

  for (k = 0; k <= 20; k++)  
    { printf("numbers :  k = %2d ; k*step = %f ;", k, k*step); 
      snprintf(name1,length+1,"%f", iterMax+k*step); /* */
      snprintf(name2,length+1, " %f", k*step); /*  */
      printf("strings : k*step =  %s ; iterMax+k*step = %s \n",name2, name1);  
    }
  return 0;
}

编译:

 gcc t.c  -Wall

输出是:

./a.out 
numbers :  k =  0 ; k*step = 0.000000 ;strings : k*step =   0.000000 ; iterMax+k*step = 100000000000.000000 
numbers :  k =  1 ; k*step = 0.000010 ;strings : k*step =   0.000010 ; iterMax+k*step = 100000000000.000015 
numbers :  k =  2 ; k*step = 0.000020 ;strings : k*step =   0.000020 ; iterMax+k*step = 100000000000.000015 
numbers :  k =  3 ; k*step = 0.000030 ;strings : k*step =   0.000030 ; iterMax+k*step = 100000000000.000031 
numbers :  k =  4 ; k*step = 0.000040 ;strings : k*step =   0.000040 ; iterMax+k*step = 100000000000.000046 

当 iterMax 较小时,结果相同(逗号后的数字),例如 100000000:

numbers :  k =  0 ; k*step = 0.000000 ;strings : k*step =   0.000000 ; iterMax+k*step = 100000000.000000 
numbers :  k =  1 ; k*step = 0.000010 ;strings : k*step =   0.000010 ; iterMax+k*step = 100000000.000010 
numbers :  k =  2 ; k*step = 0.000020 ;strings : k*step =   0.000020 ; iterMax+k*step = 100000000.000020 
numbers :  k =  3 ; k*step = 0.000030 ;strings : k*step =   0.000030 ; iterMax+k*step = 100000000.000030 
numbers :  k =  4 ; k*step = 0.000040 ;strings : k*step =   0.000040 ; iterMax+k*step = 100000000.000040 

ULLONG_MAX = 18446744073709551615 大于 iterMax。

我该如何解决?

TIA

【问题讨论】:

  • 你到底想要什么?
  • 您期望非常大的浮点数在非常小的十进制末端非常精确。这不是浮点的工作方式。请参阅stackoverflow.com/questions/9765744/precision-in-c-floats,以及更多关于浮点精度的问题。
  • 您正在使用snprintf 并明确要求它超出您的缓冲区? 为什么???
  • 如果snprintf() 的第一个参数是char buffer[SOMESIZE];,第二个参数通常应该是sizeof(buffer)。指定比数组大小更大的值会引发 snprintf() 旨在防止的缓冲区溢出,并且在正确使用时确实可以防止。

标签: c gcc printf


【解决方案1】:

这其实是double精度的问题。还有很多其他问题可以解释更多有关 IEEE-754 浮点数的信息,但我将在这里总结相关要点:

  1. double 和家人以有限的精度有效地以科学计数法存储数字。这意味着数字越大,准确度就越低。
  2. 大多数数字使用基数 2。因此,十进制 0.1 不能精确存储(相反,它类似于 0.10000000149011612

因此,数字100000000000.000010 是“大”的,因此它在小数点后变得不太准确。事实上,一旦你接近4503599627370496,你甚至不能存储所有整数!

【讨论】:

  • 参考“它们以 2 为基数,而不是 10 基数工作。”:double 使用基数 FLT_RADIX通常 2IEEE-754 定义以 2 为基数和以 10 为基数的数字。二进制格式更为常见。
  • snprintf(name1,length+1,"%f", log10(iterMax)+k*step);效果很好。谢谢
【解决方案2】:

转换为long double 以获得更高的精度:

snprintf(name1,length+1,"%Lf", (long double)iterMax+k*step); 

输出:

numbers :  k =  0 ; k*step = 0.000000 ;strings : k*step =   0.000000 ; iterMax+k*step = 100000000000.000000 
numbers :  k =  1 ; k*step = 0.000010 ;strings : k*step =   0.000010 ; iterMax+k*step = 100000000000.000010 
numbers :  k =  2 ; k*step = 0.000020 ;strings : k*step =   0.000020 ; iterMax+k*step = 100000000000.000020 
numbers :  k =  3 ; k*step = 0.000030 ;strings : k*step =   0.000030 ; iterMax+k*step = 100000000000.000030 
numbers :  k =  4 ; k*step = 0.000040 ;strings : k*step =   0.000040 ; iterMax+k*step = 100000000000.000040 
numbers :  k =  5 ; k*step = 0.000050 ;strings : k*step =   0.000050 ; iterMax+k*step = 100000000000.000050 
numbers :  k =  6 ; k*step = 0.000060 ;strings : k*step =   0.000060 ; iterMax+k*step = 100000000000.000060 
numbers :  k =  7 ; k*step = 0.000070 ;strings : k*step =   0.000070 ; iterMax+k*step = 100000000000.000070 
numbers :  k =  8 ; k*step = 0.000080 ;strings : k*step =   0.000080 ; iterMax+k*step = 100000000000.000080 
numbers :  k =  9 ; k*step = 0.000090 ;strings : k*step =   0.000090 ; iterMax+k*step = 100000000000.000090 
numbers :  k = 10 ; k*step = 0.000100 ;strings : k*step =   0.000100 ; iterMax+k*step = 100000000000.000100 
numbers :  k = 11 ; k*step = 0.000110 ;strings : k*step =   0.000110 ; iterMax+k*step = 100000000000.000110 
numbers :  k = 12 ; k*step = 0.000120 ;strings : k*step =   0.000120 ; iterMax+k*step = 100000000000.000120 
numbers :  k = 13 ; k*step = 0.000130 ;strings : k*step =   0.000130 ; iterMax+k*step = 100000000000.000130 
numbers :  k = 14 ; k*step = 0.000140 ;strings : k*step =   0.000140 ; iterMax+k*step = 100000000000.000140 
numbers :  k = 15 ; k*step = 0.000150 ;strings : k*step =   0.000150 ; iterMax+k*step = 100000000000.000150 
numbers :  k = 16 ; k*step = 0.000160 ;strings : k*step =   0.000160 ; iterMax+k*step = 100000000000.000160 
numbers :  k = 17 ; k*step = 0.000170 ;strings : k*step =   0.000170 ; iterMax+k*step = 100000000000.000170 
numbers :  k = 18 ; k*step = 0.000180 ;strings : k*step =   0.000180 ; iterMax+k*step = 100000000000.000180 
numbers :  k = 19 ; k*step = 0.000190 ;strings : k*step =   0.000190 ; iterMax+k*step = 100000000000.000190 
numbers :  k = 20 ; k*step = 0.000200 ;strings : k*step =   0.000200 ; iterMax+k*step = 100000000000.000200 

【讨论】:

  • 如果编译器不支持%Lf,它可能也不支持long double
  • snprintf(name1,length+1,"%Lf", (long double) iterMax+k*step) 对我有用。
  • 即使支持long double,它可能没有比double更多的范围/精度。可能 OP 没有这么弱的编译器。
  • @Adam 使用length+1 可能会导致麻烦。见乔纳森莱弗勒评论。最好使用snprintf(name1, sizeof name1, ...
【解决方案3】:

当打印超过DBL_DIG 有效十进制数字时,可能会出现double 有限格式的影响。

例子:

#include <float.h>

printf("%d\n", DBL_DIG);
printf("%.*e\n", DBL_DIG - 1, 100000000000.0 + 0.00001);
printf("%.*e\n", DBL_DIG - 1 + 10, 100000000000.0 + 0.00001);

15
1.00000000000000e+11
1.000000000000000152587891e+11

1.00000000000000e+11 有 15 个有效十进制数字。 ('.' 右侧 14)


不管double 使用的基数(2、10、16 等)如何,只有这么多十进制数字 可以“往返”。

例子:(假设DBL_DIG为10,C指定的最小值

double x;
scanf("%lf", &x);
printf("%.*e\n", DBL_DIG - 1, x);
printf("%.*e\n", DBL_DIG - 1 + 5, x);

如果用户输入“12345678901234567890”,输出将是“1.234567890e19”和“1.234567890?????e19”,“?????”不是由 C 指定的开头。

【讨论】:

    猜你喜欢
    • 2017-06-28
    • 2020-01-22
    • 1970-01-01
    • 1970-01-01
    • 2013-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多