3

在尝试使用一些 perlin 噪声函数时遇到了一个奇怪的错误。突然间,我在所有通话中都得到了一个off value。将其追溯到 fmod() 中不一致的返回值。

i=512;
cout << i << ","  << fmod(i/102.4,1.f) << "," << fmod(i/102.4,1.f) << endl;

我希望得到以下输出。

512,0,0

但我没有。我明白了。

512,0,1

更奇怪的是,如果我多次调用 cout 行,错误会在以后的运行中消失。

512,0,1
512,0,0
512,0,0

将值硬编码到函数中(将所有 'y' 替换为 '512')会产生正确的结果(总是返回 0)。

有没有人见过这样的结果?

4

2 回答 2

5

最终编辑:这几乎可以肯定是 GCC 或 glibc 中的错误。只需在代码中的其他地方声明一个调用 fmod 的函数(即使该函数没有被调用),问题就会消失。

我建议传递-fno-builtins给编译器来解决这个问题。这似乎为我解决了这个问题。


我能够在 GCC 4.5.3 中为 cygwin 重现这一点。

仅当i变量是非常量时才会发生这种情况[编辑:可能是因为它鼓励编译器使用临时来存储初始除法的结果)。我要测试的更简单的 C 程序:

#include <stdio.h>

#include <math.h>

int main()
{
    int i = 512;
    printf("%f %f\n", fmod(i/102.4,1.f), fmod(i/102.4,1.f));
    printf("%f %f\n", fmod(i/102.4,1.f), fmod(i/102.4,1.f));

}

哪个输出:

0.000000 1.000000
0.000000 0.000000

我试过查看 asm 输出,但我的 x87-foo 很弱。 其他人可以看到有什么问题吗? 在此期间我会继续寻找:

    .file   "test.cpp"
    .section    .debug_abbrev,"dr"
Ldebug_abbrev0:
    .section    .debug_info,"dr"
Ldebug_info0:
    .section    .debug_line,"dr"
Ldebug_line0:
    .text
Ltext0:
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC3:
    .ascii "%f %f\12\0"
    .text
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB7:
    .file 1 "test.cpp"
    .loc 1 6 0
    pushl   %ebp
LCFI0:
    movl    %esp, %ebp
LCFI1:
    andl    $-16, %esp
LCFI2:
    subl    $64, %esp
LCFI3:
    .loc 1 6 0
    call    ___main
LBB2:
    .loc 1 7 0
    movl    $512, 60(%esp)
    .loc 1 8 0
    fildl   60(%esp)
    fldl    LC0
    fdivrp  %st, %st(1)
    fld1
    fld %st(1)
L2:
    fprem
    fnstsw  %ax
    sahf
    jp  L2
    fstp    %st(1)
    fucomi  %st(0), %st
    jp  L5
    fucomi  %st(0), %st
    je  L6
    fstp    %st(0)
    jmp L4
L5:
    fstp    %st(0)
L4:
    fld1
    fstpl   8(%esp)
    fstpl   (%esp)
    call    _fmod
    jmp L3
L6:
    fstp    %st(1)
L3:
    fstpl   40(%esp)
    fildl   60(%esp)
    fldl    LC0
    fdivrp  %st, %st(1)
    fld1
    fstpl   8(%esp)
    fstpl   (%esp)
    call    _fmod
    fldl    40(%esp)
    fstpl   12(%esp)
    fstpl   4(%esp)
    movl    $LC3, (%esp)
    call    _printf
    .loc 1 9 0
    fildl   60(%esp)
    fldl    LC0
    fdivrp  %st, %st(1)
    fld1
    fstpl   8(%esp)
    fstpl   (%esp)
    call    _fmod
    fstpl   32(%esp)
    fildl   60(%esp)
    fldl    LC0
    fdivrp  %st, %st(1)
    fld1
    fstpl   8(%esp)
    fstpl   (%esp)
    call    _fmod
    fldl    32(%esp)
    fstpl   12(%esp)
    fstpl   4(%esp)
    movl    $LC3, (%esp)
    call    _printf
LBE2:
    movl    $0, %eax
    .loc 1 10 0
    leave
LCFI4:
    ret
LFE7:
    .section .rdata,"dr"
    .align 8
LC0:
    .long   -1717986918
    .long   1079613849
    .section    .debug_frame,"dr"
Lframe0:
    .long   LECIE0-LSCIE0
LSCIE0:
    .long   0xffffffff
    .byte   0x1
    .ascii "\0"
    .uleb128 0x1
    .sleb128 -4
    .byte   0x8
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .byte   0x88
    .uleb128 0x1
    .align 4
LECIE0:
LSFDE0:
    .long   LEFDE0-LASFDE0
LASFDE0:
    .secrel32   Lframe0
    .long   LFB7
    .long   LFE7-LFB7
    .byte   0x4
    .long   LCFI0-LFB7
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   LCFI1-LCFI0
    .byte   0xd
    .uleb128 0x5
    .byte   0x4
    .long   LCFI4-LCFI1
    .byte   0xc5
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .align 4
LEFDE0:
    .section    .eh_frame,"w"
Lframe1:
    .long   LECIE1-LSCIE1
LSCIE1:
    .long   0x0
    .byte   0x1
    .ascii "\0"
    .uleb128 0x1
    .sleb128 -4
    .byte   0x8
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .byte   0x88
    .uleb128 0x1
    .align 4
LECIE1:
LSFDE3:
    .long   LEFDE3-LASFDE3
LASFDE3:
    .long   LASFDE3-Lframe1
    .long   LFB7
    .long   LFE7-LFB7
    .byte   0x4
    .long   LCFI0-LFB7
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   LCFI1-LCFI0
    .byte   0xd
    .uleb128 0x5
    .byte   0x4
    .long   LCFI4-LCFI1
    .byte   0xc5
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .align 4
LEFDE3:
    .text
Letext0:
    .section    .debug_loc,"dr"
Ldebug_loc0:
LLST0:
    .long   LFB7-Ltext0
    .long   LCFI0-Ltext0
    .word   0x2
    .byte   0x74
    .sleb128 4
    .long   LCFI0-Ltext0
    .long   LCFI1-Ltext0
    .word   0x2
    .byte   0x74
    .sleb128 8
    .long   LCFI1-Ltext0
    .long   LCFI4-Ltext0
    .word   0x2
    .byte   0x75
    .sleb128 8
    .long   LCFI4-Ltext0
    .long   LFE7-Ltext0
    .word   0x2
    .byte   0x74
    .sleb128 4
    .long   0x0
    .long   0x0
    .section    .debug_info,"dr"
    .long   0x13a
    .word   0x2
    .secrel32   Ldebug_abbrev0
    .byte   0x4
    .uleb128 0x1
    .ascii "GNU C++ 4.5.3\0"
    .byte   0x4
    .ascii "test.cpp\0"
    .ascii "/home/martin\0"
    .long   Ltext0
    .long   Letext0
    .secrel32   Ldebug_line0
    .uleb128 0x2
    .byte   0x4
    .byte   0x7
    .ascii "unsigned int\0"
    .uleb128 0x2
    .byte   0x1
    .byte   0x6
    .ascii "char\0"
    .uleb128 0x2
    .byte   0x1
    .byte   0x6
    .ascii "signed char\0"
    .uleb128 0x2
    .byte   0x1
    .byte   0x8
    .ascii "unsigned char\0"
    .uleb128 0x2
    .byte   0x2
    .byte   0x5
    .ascii "short int\0"
    .uleb128 0x2
    .byte   0x2
    .byte   0x7
    .ascii "short unsigned int\0"
    .uleb128 0x2
    .byte   0x4
    .byte   0x5
    .ascii "int\0"
    .uleb128 0x2
    .byte   0x8
    .byte   0x5
    .ascii "long long int\0"
    .uleb128 0x2
    .byte   0x8
    .byte   0x7
    .ascii "long long unsigned int\0"
    .uleb128 0x2
    .byte   0x4
    .byte   0x5
    .ascii "long int\0"
    .uleb128 0x2
    .byte   0x4
    .byte   0x7
    .ascii "long unsigned int\0"
    .uleb128 0x2
    .byte   0x8
    .byte   0x4
    .ascii "double\0"
    .uleb128 0x2
    .byte   0x4
    .byte   0x4
    .ascii "float\0"
    .uleb128 0x2
    .byte   0xc
    .byte   0x4
    .ascii "long double\0"
    .uleb128 0x3
    .byte   0x1
    .ascii "main\0"
    .byte   0x1
    .byte   0x5
    .long   0x98
    .long   LFB7
    .long   LFE7
    .secrel32   LLST0
    .uleb128 0x4
    .long   LBB2
    .long   LBE2
    .uleb128 0x5
    .ascii "i\0"
    .byte   0x1
    .byte   0x7
    .long   0x98
    .byte   0x2
    .byte   0x74
    .sleb128 60
    .byte   0x0
    .byte   0x0
    .byte   0x0
    .section    .debug_abbrev,"dr"
    .uleb128 0x1
    .uleb128 0x11
    .byte   0x1
    .uleb128 0x25
    .uleb128 0x8
    .uleb128 0x13
    .uleb128 0xb
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x1b
    .uleb128 0x8
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x10
    .uleb128 0x6
    .byte   0x0
    .byte   0x0
    .uleb128 0x2
    .uleb128 0x24
    .byte   0x0
    .uleb128 0xb
    .uleb128 0xb
    .uleb128 0x3e
    .uleb128 0xb
    .uleb128 0x3
    .uleb128 0x8
    .byte   0x0
    .byte   0x0
    .uleb128 0x3
    .uleb128 0x2e
    .byte   0x1
    .uleb128 0x3f
    .uleb128 0xc
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x40
    .uleb128 0x6
    .byte   0x0
    .byte   0x0
    .uleb128 0x4
    .uleb128 0xb
    .byte   0x1
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .byte   0x0
    .byte   0x0
    .uleb128 0x5
    .uleb128 0x34
    .byte   0x0
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x2
    .uleb128 0xa
    .byte   0x0
    .byte   0x0
    .byte   0x0
    .section    .debug_pubnames,"dr"
    .long   0x17
    .word   0x2
    .secrel32   Ldebug_info0
    .long   0x13e
    .long   0x10d
    .ascii "main\0"
    .long   0x0
    .section    .debug_pubtypes,"dr"
    .long   0xe
    .word   0x2
    .secrel32   Ldebug_info0
    .long   0x13e
    .long   0x0
    .section    .debug_aranges,"dr"
    .long   0x1c
    .word   0x2
    .secrel32   Ldebug_info0
    .byte   0x4
    .byte   0x0
    .word   0x0
    .word   0x0
    .long   Ltext0
    .long   Letext0-Ltext0
    .long   0x0
    .long   0x0
    .def    _fmod;  .scl    2;  .type   32; .endef
    .def    _printf;    .scl    2;  .type   32; .endef

[编辑:注意,总是第一次调用 fmod 会返回奇怪的结果(永远不会是以后的)。正在从右到左评估 fmod 调用。

[编辑2:定义一个函数,double my_fmod(double a, double b) { return fmod(a, b); }并通过调用使问题消失。]

于 2012-08-05T08:20:07.097 回答
3

这不是 gcc 或库错误。这只是不精确的浮点表示的结果。

i=512;
cout << i << ","  << fmod(i/102.4,1.f) << "," << fmod(i/102.4,1.f) << endl;

数学结果512/102.4正好是 5。但102.4不能用二进制浮点数精确表示,因此传递给的值fmod可能正好是5.0,也可能略高于或低于它。

如果除法的结果是 ,4.99999999那么fmod(i/102.4,1.f)将返回0.99999999,它可能会在输出时四舍五入到1

尝试更精确地显示该值。

您需要修改您的代码,以便它允许的值比您预期的要小得多。由于fmod()是一个不连续的函数,它可以极大地放大这种差异。

这个程序:

#include <iostream>
#include <iomanip>
#include <cmath>
int main(void) {
    int i = 512;
    std::cout << i << "," 
              << std::setprecision(20) << fmod(i/102.4,1.f)
              << "," << fmod(i/102.4,1.f) << std::endl;
    return 0;
}

在我的系统上产生这个输出:

512,0,0.99999999999999977796
于 2012-08-05T09:00:19.263 回答