【问题标题】:Why does gdb evaluate sqrt(3) to 0?为什么 gdb 将 sqrt(3) 评估为 0?
【发布时间】:2011-07-04 14:25:15
【问题描述】:

3 的平方根,由 Wolfram Alpha 估计:

1.7320508075688772935274463415058723669428052538103806280558...

当我在 C 中执行 sqrt(3) 时,它的计算结果为 0。为什么?

EDIT4:这是在 GDB 中重现此问题的方法。创建test.c如下:

#include <stdio.h>                                                                                                                                                                      
#include <math.h>

int main()
{
  printf("sqrt(3): %f\n", sqrt(3));
  return 0;
}

编译:

gcc -O0 -g -Wall -pedantic -ansi -lm -o test test.c

运行调试器:

gdb test

在控制台输入:

(gdb) break test.c:6
Breakpoint 1 at 0x400578: file test.c, line 6.
(gdb) r
Starting program: /home/pdedecker/Desktop/test   
Breakpoint 1, main () at test.c:6
6         printf("sqrt(3): %f\n", sqrt(3));
(gdb) print sqrt(3)
$1 = 0
(gdb) s
sqrt(3): 1.732051

我的 GDB 版本是GNU gdb (GDB) SUSE (7.1-3.12)

【问题讨论】:

  • 向我们展示您用于获取此结果的代码。
  • 你包括&lt;math.h&gt;吗?
  • 是的,我确实包含了 math.h 并添加了适当的 -lrt 标志。
  • 显然,Wolfram Alpha 是错误的。 *耸耸肩*
  • 也许您的调试器没有正确评估表达式。

标签: c eclipse math gdb


【解决方案1】:

问题不在于缺少函数声明(没有丢失,因为您确实包含了&lt;math.h&gt;)。

问题是缺少您实际使用的sqrt 的调试信息。如果没有该调试信息,GDB 不知道要传递给sqrt() 的参数类型以及它返回的内容。

您可以通过安装 libc-debuginfo 包在许多 Linux 发行版上获取所需的调试信息。这是我在这样的系统上看到的:

gdb -q ./a.out
Reading symbols from /tmp/a.out...done.
(gdb) b main
Breakpoint 1 at 0x400558: file t.c, line 6.
(gdb) r

Breakpoint 1, main () at t.c:6
6     printf("sqrt(3): %f\n", sqrt(3));
(gdb) p sqrt
$1 = {<text variable, no debug info>} 0x7ffff7b7fb50 <__sqrt>

注意:“没有调试信息”

(gdb) p sqrt(3)
$2 = 0
(gdb) p sqrt(3.0)
$3 = 0

注意:符合你的行为。 哪些sqrt 函数做了 有调试信息?

(gdb) info func sqrt
All functions matching regular expression "sqrt":

File ../sysdeps/x86_64/fpu/e_sqrt.c:
double __ieee754_sqrt(double);

File s_csqrt.c:
complex double __csqrt(complex double);

File ../sysdeps/x86_64/fpu/e_sqrtf.c:
float __ieee754_sqrtf(float);

File w_sqrtf.c:
float __sqrtf(float);

File s_csqrtf.c:
complex float __csqrtf(complex float);

File ../sysdeps/i386/fpu/e_sqrtl.c:
long double __ieee754_sqrtl(long double);

File w_sqrtl.c:
long double __sqrtl(long double);

File s_csqrtl.c:
complex long double __csqrtl(complex long double);

File ../sysdeps/ieee754/dbl-64/mpsqrt.c:
void __mpsqrt(mp_no *, mp_no *, int);

File w_sqrt.c:
double __sqrt(double);

(gdb) p __sqrt
$4 = {double (double)} 0x7ffff7b7fb50 <__sqrt>

注意:__sqrtsqrt 在同一个地址,但 GDB 知道它的类型!

(gdb) p __sqrt(3)
$5 = 1.7320508075688772
(gdb) p __sqrt(3.0)
$6 = 1.7320508075688772

可以合理地认为这是 GDB 中的一个错误。随意在GDB bugzilla 中创建一个。

【讨论】:

  • 有趣。我继续安装了glibc-debuginfo(希望这就是你的意思,因为我找不到libc-debuginfo),我还看到了__sqrt 的函数原型。 print __sqrt(3) 确实返回了一个有效的结果。我会将此错误报告给 GDB 开发人员,但我几乎没有报告错误的经验,而且我对 GDB 的内部工作方式了解不多,无法向开发人员提供有用的信息。无论如何,谢谢...我将您的答案标记为已接受!
【解决方案2】:

我预测你没有做#include &lt;math.h&gt;

如果没有函数声明,C 将默认函数的返回值为int。根据 int 的大小,浮点数可能会返回为 0。 C 也不知道如何转换函数参数。它将默认将参数作为它碰巧的任何类型传递。如果将整数传递给sqrt(),它将不会被转换为双精度,但sqrt() 函数会将位模式解释为double

【讨论】:

  • +1,不仅返回值变得疯狂。如果没有原型,参数也不会转换为double。所以sqrt 会看到一些奇怪的位模式,它解释为double,然后在寄存器中返回一个双精度值,或者调用者在其他(整数)寄存器中获取值。
  • 我确实包含了math.h,并且我确实链接了-lm
【解决方案3】:

要在没有调试信息的情况下调用函数,您必须使用函数指针转换显式地告诉 gdb 返回和参数的类型。因此,对于您的示例:

(gdb) print ((double (*) (double)) sqrt) (3)
$1 = 1.7320508075688772

【讨论】:

  • print (double) sqrt((double)3) 返回 $6 = 1 我怀疑结果首先转换为 int 然后再转换为 double ..
【解决方案4】:
#include <stdio.h>                                                                                                                                                                      
#include <math.h>

int main()
{
  printf("sqrt(3): %f\n", sqrt(3));

  return 0;
}

输出:

josh@josh-ubuntu:~/scratch$ ./a.out 
sqrt(3): 1.732051

【讨论】:

  • 你能在 GDB 中重现这个问题吗?启动 GDB 并在控制台中输入print sqrt(3.0)
【解决方案5】:

可能不支持调用 sqrt !也许是因为它是一个 libc 函数。我不知道其中的深层原因,但以下测试显示了一个有趣的行为:

double mysqrt(double x) { return sqrt(x) };

然后在一个 gdb 会话中:

(gdb) p mysqrt(3)
$1 = 1.7320508075688772
(gdb) p sqrt(3)
$2 = -1209775368

【讨论】:

    猜你喜欢
    • 2019-05-24
    • 1970-01-01
    • 2021-11-01
    • 2016-08-25
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 2018-08-16
    相关资源
    最近更新 更多