为什么我在C中使用log()函数得到不同的结果?

时间:2018-01-12 03:18:16

标签: c gcc floating-point raspberry-pi3

以下是log()函数测试的一个简单示例:

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

int main(void)
{
    int a = 2;
    printf("int a = %d, log((double)a) = %g, log(2.0) = %g\n", a, log((double)a), log(2.0));
    return 0;
}

我在Raspberry Pi 3和Ubuntu16.04上有所不同:

臂-Linux的gnueabi-GCC

$ arm-linux-gnueabi-gcc -mfloat-abi=soft -march=armv7-a  foo.c -o foo -lm
$ ./foo 
int a = 2, log((double)a) = 5.23028e-314, log(2.0) = 0.693147

臂-Linux的gnueabihf-GCC

$ arm-linux-gnueabihf-gcc -march=armv7-a  foo.c -o foo -lm
$ ./foo 
int a = 2, log((double)a) = 0.693147, log(2.0) = 0.693147

GCC

$ gcc foo.c -o foo -lm
$ ./foo 
int a = 2, log((double)a) = 0.693147, log(2.0) = 0.693147

2 个答案:

答案 0 :(得分:1)

Raspbian的标准发行版使用Raspberry Pi(Raspbian FAQ)的硬件浮点支持,这与使用软件库仅使用整数模拟浮点计算的不同方法不完全兼容。

您可以通过查找硬浮点型目录/lib/arm-linux-gnueabihf和软浮点型目录/lib/arm-linux-gnueabiHow can I tell...)来判断Raspbian发行版的类型。

正如Pascal Cuoq在其中一个评论中指出的那样,可能有兴趣知道在所有示例中log(2.0)的正确结果的原因称为常量折叠 。允许编译器在编译时计算每个结果 - 如果可能 - 用于优化目的。如果您的代码中有不同的舍入模式,这可能是一种不需要的行为。 GCC有-frounding-math来切换常量折叠(除其他外),虽然它可能无法捕捉到所有内容,所以在这里要小心。

答案 1 :(得分:0)

无法重复此问题。你的反汇编在哪里显示给printf的值?

#include <math.h>
double fun1 ( void )
{
    return(log(2));
}
double fun2 ( void )
{
    return(log(2.0));
}


00000000 <fun1>:
   0:   e30309ef    movw    r0, #14831  ; 0x39ef
   4:   e3021e42    movw    r1, #11842  ; 0x2e42
   8:   e34f0efa    movt    r0, #65274  ; 0xfefa
   c:   e3431fe6    movt    r1, #16358  ; 0x3fe6
  10:   e12fff1e    bx  lr

00000014 <fun2>:
  14:   e30309ef    movw    r0, #14831  ; 0x39ef
  18:   e3021e42    movw    r1, #11842  ; 0x2e42
  1c:   e34f0efa    movt    r0, #65274  ; 0xfefa
  20:   e3431fe6    movt    r1, #16358  ; 0x3fe6
  24:   e12fff1e    bx  lr

00000000 <fun1>:
   0:   ed9f 0b01   vldr    d0, [pc, #4]    ; 8 <fun1+0x8>
   4:   4770        bx  lr
   6:   bf00        
   8:   fefa39ef    
   c:   3fe62e42    

00000010 <fun2>:
  10:   ed9f 0b01   vldr    d0, [pc, #4]    ; 18 <fun2+0x8>
  14:   4770        bx  lr
  16:   bf00        
  18:   fefa39ef    
  1c:   3fe62e42    

0000000000000000 <fun1>:
   0:   f2 0f 10 05 00 00 00    movsd  0x0(%rip),%xmm0        # 8 <fun1+0x8>
   7:   00 
   8:   c3                      retq   

0000000000000010 <fun2>:
  10:   f2 0f 10 05 00 00 00    movsd  0x0(%rip),%xmm0        # 18 <fun2+0x8>
  17:   00 
  18:   c3                      retq   


0000000000000000 <.LC0>:
   0:   ef                
   1:   39 fa             
   3:   fe 42 2e          
   6:   e6 3f             

现在导致int浮动转换与浮动版本(2)vs(2.0)中的构建以及添加(2.0F)。编译时间或运行时可能会导致差异。

首先删除printf,把这个问题分成两半,我看到一些printf的东西或者不是printf的东西。那么这是一个编译时间的东西还是这个运行时的东西,这是一个硬浮动的东西还是一个软浮动的东西。这是一个c库的东西还是不是C库的东西。

如果你到目前为止做了什么调试呢?

最终有人会将&#34;无论程序员应该知道浮点数&#34;是否适用......

修改

#include <math.h>
double fun ( void )
{
    return(log(2.0));
}

00000000 <fun>:
   0:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
   4:   e28db000    add fp, sp, #0
   8:   e30329ef    movw    r2, #14831  ; 0x39ef
   c:   e34f2efa    movt    r2, #65274  ; 0xfefa
  10:   e3023e42    movw    r3, #11842  ; 0x2e42
  14:   e3433fe6    movt    r3, #16358  ; 0x3fe6
  18:   ec432b17    vmov    d7, r2, r3
  1c:   eeb00b47    vmov.f64    d0, d7
  20:   e24bd000    sub sp, fp, #0
  24:   e49db004    pop {fp}        ; (ldr fp, [sp], #4)
  28:   e12fff1e    bx  lr

00000000 <fun>:
   0:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
   4:   e28db000    add fp, sp, #0
   8:   e30329ef    movw    r2, #14831  ; 0x39ef
   c:   e34f2efa    movt    r2, #65274  ; 0xfefa
  10:   e3023e42    movw    r3, #11842  ; 0x2e42
  14:   e3433fe6    movt    r3, #16358  ; 0x3fe6
  18:   e1a00002    mov r0, r2
  1c:   e1a01003    mov r1, r3
  20:   e24bd000    sub sp, fp, #0
  24:   e49db004    pop {fp}        ; (ldr fp, [sp], #4)
  28:   e12fff1e    bx  lr

然而,持续折叠的概念解释了为什么调用log()会产生截然不同的结果。 (可以说是工具链的不同版本(或不同的命令行参数)你可能会很幸运,到目前为止我们还不知道工具链,构建选项等的版本是否能够重复这个)。

编辑2

#include <math.h>
double fun ( void )
{
    return(log(2));
}

00000000 <fun>:
   0:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
   4:   e28db000    add fp, sp, #0
   8:   e30329ef    movw    r2, #14831  ; 0x39ef
   c:   e34f2efa    movt    r2, #65274  ; 0xfefa
  10:   e3023e42    movw    r3, #11842  ; 0x2e42
  14:   e3433fe6    movt    r3, #16358  ; 0x3fe6
  18:   ec432b17    vmov    d7, r2, r3
  1c:   eeb00b47    vmov.f64    d0, d7
  20:   e24bd000    sub sp, fp, #0
  24:   e49db004    pop {fp}        ; (ldr fp, [sp], #4)
  28:   e12fff1e    bx  lr




00000000 <fun>:
   0:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
   4:   e28db000    add fp, sp, #0
   8:   e30329ef    movw    r2, #14831  ; 0x39ef
   c:   e34f2efa    movt    r2, #65274  ; 0xfefa
  10:   e3023e42    movw    r3, #11842  ; 0x2e42
  14:   e3433fe6    movt    r3, #16358  ; 0x3fe6
  18:   e1a00002    mov r0, r2
  1c:   e1a01003    mov r1, r3
  20:   e24bd000    sub sp, fp, #0
  24:   e49db004    pop {fp}        ; (ldr fp, [sp], #4)
  28:   e12fff1e    bx  lr

大约60秒的工作量来考虑不断折叠可能是一个因素,到目前为止它并不适用,但是那里有潜在的愚蠢运气,但同样的愚蠢运气可能/将适用于两次调用记录

OP为反汇编该程序而进行的几秒钟工作将很快涵盖这一主题。