2

你可以说我对 C 比较陌生,但我需要澄清一个问题。我有一个 char[] 代表一个数字。如果这个 char[] 比 LONG_MAX 长,我想告诉用户它太长了。问题是当我将它的值与浮点数进行比较时,它会被截断。这就是我的意思。

int main(int argc, char ** argv) {

  char str[] = argv[1]; /* I set it to 9223372036854775809, only +1 higher than LONG_MAX */ 
  double l = atof(str); 
  double j = LONG_MAX; 

  printf("%lf\n", l); /* This prints 9223372036854775808.000000, which is LONG_MAX ??? WHY?? */
  printf("%lf\n", j); /* This prints same as above, 9223372036854775808.000000 */ 

  printf("%s\n", l > j ? "true" : "false"); /* false */

  return 0; /* what am I doing wrong? */

}

更新:

我尝试了您的 iret 解决方案,但仍然遇到相同的舍入问题

 j = LONG_MAX; 
  int iret = sscanf (str, "%lf", &l);
  if (iret != 1)
    return 0; /* conversion was bad */
  else {
    if (l > j || l < -(j))
      return 0; /* too small or too large */
  }

  printf("%lf\n", l); 
  printf("%lf\n", j);

  printf("%s\n", l > j ? "true" : "false");
4

4 回答 4

1

使用 可以很容易地检查溢出strtol,但它需要一些额外的工作。

const char *str = ...;
char *e;
long x;

errno = 0;
x = strtol(str, &e, 0);
if (!*str || *e) {
    fprintf(stderr, "invalid number: %s\n", str);
    exit(1);
}
if ((x == LONG_MAX || x == LONG_MIN) && errno == ERANGE) {
    fprintf(stderr, "number too large: %s\n", str);
    exit(1);
}

现在,让我们谈谈strtod(or atof,这只是 ) 的损坏版本的问题strtod

如果转换9223372036854775809为a double,那么9223372036854775808正确的。Adouble具有 53 位精度,而 64 位long具有 64 位。一旦开始使用浮点数,就需要为舍入做好准备。

例如,以下代码中存在舍入错误。你能发现吗?

double x = 0.1;

脚注:我在这里假设 64 位long和 IEEE 双精度double

于 2012-10-03T04:36:01.020 回答
0

根据 atof 的“man”页面:

http://linux.die.net/man/3/atof

atof() 函数将 nptr 指向的字符串的初始部分转换为双精度。... atof() 不检测错误。

这就是为什么我更喜欢使用“sscanf”的原因:

int iret = sscanf (SOME_TEXT, "%lf", &SOME_DOUBLE);

如果“iret != 1”,那么我知道发生了错误,并且可以采取适当的措施。

恕我直言...

PS:

你为什么不声明“l”和“j”。调皮调皮!您应该始终声明您的变量,即使在 FORTRAN 和 Basic 中也是如此;)

为什么不将它们声明为 float ("%f") 或 double ("%lf")?

于 2012-10-03T04:06:27.460 回答
0

您的 long 似乎是 63 位。浮点数有 23 个有效位(或 24 个带前导 1)。您的 long 在高 24 位中完全相同。所以他们变得一样。浮点数基本上是尾数* 2 ^ 指数。指数确保幅度仍然匹配。但是你只有 23 位尾数。所以它变成了top23bitsoflong*2^(numberofremainingbits)。忽略前导偏差和指数偏差,这有点简化。

于 2012-10-04T04:48:38.283 回答
0

我相信您的两个输入都获得值“9223372036854775808.000000”的原因是由于浮点近似的精度限制。

顺便说一句,值不是LONG_MAX,而是LONG_MAX + 1 -> 2^63

根据定义,LONG_MAX精确的表示(作为整数)需要 63 位精度。但是,在 64 位浮点表示中double只有 53 位精度,因为需要其他位来存储符号和指数。这就是为什么两者都LONG_MAXLONG_MAX + 2舍入到9223372036854775808 <=> 2^63.

为了正确处理这种情况,也许看看strtol当输入超出范围时哪个会设置错误代码。

于 2012-10-03T04:35:45.040 回答