C 为什么isnormal()会说一个值不是';T

C 为什么isnormal()会说一个值不是';T,c,floating-point,mingw-w64,C,Floating Point,Mingw W64,显然,第二个值5.877472e-039低于正常值,因为其指数变为0,00004000 生成正确的结果: 1.175494e-038 1 00008000 5.877472e-039 1 00004000 2.938736e-039 1 00002000 1.469368e-039 1 00001000 7.346840e-040 1 00000800 3.673420e-040 1 00000400 1.836710e-040 1 00000200 9.183550e-041 1 000001

显然,第二个值
5.877472e-039
低于正常值,因为其指数变为0,
00004000

生成正确的结果:

1.175494e-038 1 00008000
5.877472e-039 1 00004000
2.938736e-039 1 00002000
1.469368e-039 1 00001000
7.346840e-040 1 00000800
3.673420e-040 1 00000400
1.836710e-040 1 00000200
9.183550e-041 1 00000100
4.591775e-041 1 00800000
2.295887e-041 1 00400000
1.147944e-041 1 00200000
5.739719e-042 1 00100000
2.869859e-042 1 00080000
1.434930e-042 1 00040000
7.174648e-043 1 00020000
3.587324e-043 1 00010000
1.793662e-043 1 80000000
8.968310e-044 1 40000000
4.484155e-044 1 20000000
2.242078e-044 1 10000000
1.121039e-044 1 08000000
5.605194e-045 1 04000000
2.802597e-045 1 02000000
1.401298e-045 1 01000000
0.000000e+000 0 00000000

我正在Windows上使用gcc(MinGW-w64)编译代码

这在其他平台(例如,在这里,在上)上也能正常工作,因此您使用的gcc/标准库版本可能存在问题


最可能的原因是,
isnormal
的参数正在转换为double。

这似乎是32位目标的MinGW-w64中的错误

我的输出:

  • MinGW-w64 x86_64-4.9.2-win32-seh-rt_v3-rev1:正确
  • MinGW-w64 i686-4.9.2-win32-dwarf-rt_v4-rev2:不正确
  • Cygwin i686 pc Cygwin(gcc 4.9.2):正确

查看MinGW-W64标题:

1.175494e-38 1 00008000
5.877472e-39 0 00004000
然后我们有:

#define isnormal(x) (fpclassify(x) == FP_NORMAL)
\uu fpclassifyf
功能实现为:

#define fpclassify(x) (sizeof (x) == sizeof (float) ? __fpclassifyf (x)   \
  : sizeof (x) == sizeof (double) ? __fpclassify (x) \
  : __fpclassifyl (x))

猜测一下:宏是否错误地将参数转换为double类型?@MattMcNabb我引用了标准,该标准规定可以对有符号或无符号字符使用hhx。字符被定义为有符号或无符号。如果你对此感到不确定,你可以自由地问一个新问题。“我认为在这个问题上,这种关于完全不同的东西的评论在这里是不合适的。”MattMcNabb我引用了标准的逐字记录。代码已定义。这与这个问题无关。如果你觉得不确定,请打开一个新问题,我不确定。@MattMcNabb这与此无关,因为如果我们假设你是正确的,那么代码就可以简单地修复。此外,你所说的与问题无关,因此是离题的。代码已定义。这是标准的完整引用,hh指定以下d、i、o、u、x或x转换说明符应用于有符号字符或无符号字符参数(该参数将根据整数升级进行升级,但其值应在打印前转换为有符号字符或无符号字符)阅读这篇文章,停止对这个问题的垃圾评论。修饰符的意思是修改说明符所做的事情;例如,
%lx
意味着我们采用
%x
的定义(从第8点开始),但将
int
更改为
long
我想知道C标准是否允许这样做(它确实说浮点运算在内部可能以比实际类型更高的精度完成)@matt:我不这么认为。分类宏描述中的措辞是,表达式首先被缩小为其语义类型,并且有一个脚注解释为什么这是必要的。我不是x87专家,但在我看来,FXAM不太可能做正确的事情。在执行该指令时,该值为单精度的事实已经丢失。Cygwin工作正常,但它链接到外部
\uuu fpclassifyf
,我不确定该值的源代码在哪里
#define fpclassify(x) (sizeof (x) == sizeof (float) ? __fpclassifyf (x)   \
  : sizeof (x) == sizeof (double) ? __fpclassify (x) \
  : __fpclassifyl (x))
  __CRT_INLINE int __cdecl __fpclassifyf (float x) {
#ifdef __x86_64__
    __mingw_flt_type_t hlp;

    hlp.x = x;
    hlp.val &= 0x7fffffff;
    if (hlp.val == 0)
      return FP_ZERO;
    if (hlp.val < 0x800000)
      return FP_SUBNORMAL;
    if (hlp.val >= 0x7f800000)
      return (hlp.val > 0x7f800000 ? FP_NAN : FP_INFINITE);
    return FP_NORMAL;
#else
    unsigned short sw;
    __asm__ __volatile__ ("fxam; fstsw %%ax;" : "=a" (sw): "t" (x));
    return sw & (FP_NAN | FP_NORMAL | FP_ZERO );
#endif
  }
    call    ___main
    flds    LC1
    fstps   24(%esp)
L7:
    flds    24(%esp)
/APP
 # 484 "F:/Prog/mingw-w64/i686-4.9.2-win32-dwarf-rt_v4-rev2/mingw32/i686-w64-mingw32/include/math.h" 1
    fxam; fstsw %ax;
 # 0 "" 2
/NO_APP
    andw    $17664, %ax
    cmpw    $1024, %ax
    sete    %al
    movzbl  %al, %eax
    movl    %eax, 12(%esp)
    fstpl   4(%esp)
    movl    $LC2, (%esp)
    call    _printf
    flds    24(%esp)
    fstps   (%esp)
    call    _PrintBytes
    movl    $LC3, (%esp)
    call    _puts
    flds    24(%esp)
/APP
 # 484 "F:/Prog/mingw-w64/i686-4.9.2-win32-dwarf-rt_v4-rev2/mingw32/i686-w64-mingw32/include/math.h" 1
    fxam; fstsw %ax;
 # 0 "" 2
/NO_APP
    andw    $17664, %ax
    cmpw    $1024, %ax
    jne L6
    fmuls   LC4
    fstps   24(%esp)
    jmp L7
L6:
    fstp    %st(0)
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE41:
    .section .rdata,"dr"
    .align 4
LC1:
    .long   8388608
    .align 4
LC4:
    .long   1056964608