为什么isnormal()说值不正常?

时间:2015-07-30 02:13:34

标签: c floating-point mingw-w64

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

void PrintBytes( const float value )
{
    const char* const byte = ( const char* )&value ;
    for( size_t i = 0 ; i < sizeof( value ) ; i++ )
    {
        printf( "%02hhx" , byte[i] );
    }   
}

int main(void) 
{
    float value = FLT_MIN;

    while( 1 )
    {
        printf( "%e %d " , value , isnormal( value ) );
        PrintBytes( value );
        puts( "" );

        if( !isnormal( value ) )
        {
            break;
        }

        value /= 2.0F;
    }
    return 0;
}

输出结果为:

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

显然,第二个值5.877472e-039是次正规的,因为它的指数变为0,00004000

Ideone会产生正确的结果:

1.175494e-38 1 00008000
5.877472e-39 0 00004000

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

2 个答案:

答案 0 :(得分:3)

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

最可能的原因是isnormal的参数正在转换为双精度。

答案 1 :(得分:0)

这似乎是具有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标题:

$('.datepicker').datepicker({
    format: 'yyyy-dd-mm',
    startDate: '-3d'
})

然后我们有:

#define isnormal(x) (fpclassify(x) == FP_NORMAL)

#define fpclassify(x) (sizeof (x) == sizeof (float) ? __fpclassifyf (x) \ : sizeof (x) == sizeof (double) ? __fpclassify (x) \ : __fpclassifyl (x)) 函数实现是:

__fpclassifyf

由于x64版似乎可行,但i686版本没有,我想我们应该归咎于上面的 __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 } 行。我为这个问题开了一个new thread;到目前为止,已经回答fxam; fstsw确实测试了一个扩展到80位精度的版本。

破损版本中fxam处生成的程序集为:

-O1