A
alok
I am getting inconsistent behvior on Linux and Solaris platfors while
computing doule ( 64 bit precision ) multiplications.
I have following two double numbers whose integer representation
is as following
I have a union
typedef union {
double double_val;
unsigned long long uint_val;
} double_reg;
double_reg a, b;
a.uint_val = 0x000ffffffffffff8ULL;
b.uint_val = 0xbff0000000000001ULL;
c = a.double_val * b.double_val;
Solaris raises UNDERFLOW Exception on this but Linux does not.
On Linux i am using following function from fenv.h to check the
exceptions on solaris.
fetestexcept (FE_ALL_EXCEPT)
The rounding mode for the above multiplication is round to Minus
Infinity which is set by
fesetround(FE_DOWNWARD)
Please suggest what am i doing wrong or i am missing any compile switch
such as to get Underflow exception on Linux as well. I am using gcc
version 3.3.3
THANKS IN ADVANCE.
Following is the Linux Program dmul_linux.c
I use
gcc dmul_linux.c -lm
command to compile the program
=======================================
#include <stdio.h>
#include <math.h>
#include <fpu_control.h>
#include <fenv.h>
// LINUX
// FE_INEXACT 0x20 inexact result
// FE_DIVBYZERO 0x4 division by zero
// FE_UNDERFLOW 0x10 result not representable due to underflow
// FE_OVERFLOW 0x8 result not representable due to overflow
// FE_INVALID 0x1 invalid operation
//FE TONEAREST IS 0
//FE UPWARD IS 800
//FE DOWNWARD IS 400
//FE TOWARDZERO IS c00
typedef union {
double double_val;
unsigned long long uint_val;
} double_reg;
int main()
{
#ifdef linux
/*
This puts the X86 FPU in 64-bit precision mode. The default
under Linux is to use 80-bit mode, which produces subtle
differences from FreeBSD and other systems, eg,
(int)(1000*atof("0.3")) is 300 in 64-bit mode, 299 in 80-bit
mode.
*/
fpu_control_t cw;
_FPU_GETCW(cw);
cw &= ~_FPU_EXTENDED;
cw |= _FPU_DOUBLE;
_FPU_SETCW(cw);
#endif
double_reg a,b,c;
int d;
a.uint_val = 0x000ffffffffffff8ULL;
b.uint_val = 0xbff0000000000001ULL;
fesetround(FE_DOWNWARD);
printf(" ROUNDING is %x \n", d);
feclearexcept(FE_ALL_EXCEPT);
d = fetestexcept (FE_ALL_EXCEPT);
printf (" D is %d \n", d);
c.double_val = (a.double_val) * (b.double_val);
d = fetestexcept (FE_ALL_EXCEPT);
printf (" D is %x \n", d);
printf (" DOUBLE RESULT = %f HEX RESULT = %llx \n", c.double_val,
c.uint_val);
};
Following is the Solaris dmul_sol.c I use
gcc dmul_sol.c
to compile the program
===================================================================
#include <ieeefp.h>
//#define FP_X_INV 0x10 /* invalid operation exception */
//#define FP_X_OFL 0x08 /* overflow exception */
//#define FP_X_UFL 0x04 /* underflow exception */
//#define FP_X_DZ 0x02 /* divide-by-zero exception */
//#define FP_X_IMP 0x01 /* imprecise (loss of precision) */
typedef union {
double double_val;
unsigned long long uint_val;
} double_reg;
int main()
{
double_reg a,b,c;
unsigned long long a_int, b_int;
int d;
a.uint_val = 0xffffffffffff8ULL;
b.uint_val = 0xbff0000000000001ULL;
a_int = 0xffffffffffff8ULL;
b_int = 0xbff0000000000001ULL;
fpsetsticky(0);
fpsetround(FP_RM);
c.double_val = (a.double_val) * (b.double_val);
//c.double_val = (double) (a_int) * (double) (b_int);
d = fpgetsticky();
printf (" EXCEPTIONS are %d \n", d);
printf (" DOUBLE RESULT = %lf HEX RESULT = %llx \n", c.double_val,
*(unsigned long long *)&(c.double_val));
};
computing doule ( 64 bit precision ) multiplications.
I have following two double numbers whose integer representation
is as following
I have a union
typedef union {
double double_val;
unsigned long long uint_val;
} double_reg;
double_reg a, b;
a.uint_val = 0x000ffffffffffff8ULL;
b.uint_val = 0xbff0000000000001ULL;
c = a.double_val * b.double_val;
Solaris raises UNDERFLOW Exception on this but Linux does not.
On Linux i am using following function from fenv.h to check the
exceptions on solaris.
fetestexcept (FE_ALL_EXCEPT)
The rounding mode for the above multiplication is round to Minus
Infinity which is set by
fesetround(FE_DOWNWARD)
Please suggest what am i doing wrong or i am missing any compile switch
such as to get Underflow exception on Linux as well. I am using gcc
version 3.3.3
THANKS IN ADVANCE.
Following is the Linux Program dmul_linux.c
I use
gcc dmul_linux.c -lm
command to compile the program
=======================================
#include <stdio.h>
#include <math.h>
#include <fpu_control.h>
#include <fenv.h>
// LINUX
// FE_INEXACT 0x20 inexact result
// FE_DIVBYZERO 0x4 division by zero
// FE_UNDERFLOW 0x10 result not representable due to underflow
// FE_OVERFLOW 0x8 result not representable due to overflow
// FE_INVALID 0x1 invalid operation
//FE TONEAREST IS 0
//FE UPWARD IS 800
//FE DOWNWARD IS 400
//FE TOWARDZERO IS c00
typedef union {
double double_val;
unsigned long long uint_val;
} double_reg;
int main()
{
#ifdef linux
/*
This puts the X86 FPU in 64-bit precision mode. The default
under Linux is to use 80-bit mode, which produces subtle
differences from FreeBSD and other systems, eg,
(int)(1000*atof("0.3")) is 300 in 64-bit mode, 299 in 80-bit
mode.
*/
fpu_control_t cw;
_FPU_GETCW(cw);
cw &= ~_FPU_EXTENDED;
cw |= _FPU_DOUBLE;
_FPU_SETCW(cw);
#endif
double_reg a,b,c;
int d;
a.uint_val = 0x000ffffffffffff8ULL;
b.uint_val = 0xbff0000000000001ULL;
fesetround(FE_DOWNWARD);
printf(" ROUNDING is %x \n", d);
feclearexcept(FE_ALL_EXCEPT);
d = fetestexcept (FE_ALL_EXCEPT);
printf (" D is %d \n", d);
c.double_val = (a.double_val) * (b.double_val);
d = fetestexcept (FE_ALL_EXCEPT);
printf (" D is %x \n", d);
printf (" DOUBLE RESULT = %f HEX RESULT = %llx \n", c.double_val,
c.uint_val);
};
Following is the Solaris dmul_sol.c I use
gcc dmul_sol.c
to compile the program
===================================================================
#include <ieeefp.h>
//#define FP_X_INV 0x10 /* invalid operation exception */
//#define FP_X_OFL 0x08 /* overflow exception */
//#define FP_X_UFL 0x04 /* underflow exception */
//#define FP_X_DZ 0x02 /* divide-by-zero exception */
//#define FP_X_IMP 0x01 /* imprecise (loss of precision) */
typedef union {
double double_val;
unsigned long long uint_val;
} double_reg;
int main()
{
double_reg a,b,c;
unsigned long long a_int, b_int;
int d;
a.uint_val = 0xffffffffffff8ULL;
b.uint_val = 0xbff0000000000001ULL;
a_int = 0xffffffffffff8ULL;
b_int = 0xbff0000000000001ULL;
fpsetsticky(0);
fpsetround(FP_RM);
c.double_val = (a.double_val) * (b.double_val);
//c.double_val = (double) (a_int) * (double) (b_int);
d = fpgetsticky();
printf (" EXCEPTIONS are %d \n", d);
printf (" DOUBLE RESULT = %lf HEX RESULT = %llx \n", c.double_val,
*(unsigned long long *)&(c.double_val));
};