cgal/Packages/Interval_arithmetic/include/CGAL/Interval_arithmetic/_FPU.h

235 lines
6.7 KiB
C

// ============================================================================
//
// Copyright (c) 1998,1999 The CGAL Consortium
//
// This software and related documentation is part of an INTERNAL release
// of the Computational Geometry Algorithms Library (CGAL). It is not
// intended for general use.
//
// ----------------------------------------------------------------------------
//
// release :
// release_date :
//
// file : include/CGAL/Interval_arithmetic/_FPU.h
// revision : $Revision$
// revision_date : $Date$
// package : Interval Arithmetic
// author(s) : Sylvain Pion <Sylvain.Pion@sophia.inria.fr>
//
// coordinator : INRIA Sophia-Antipolis <Mariette.Yvinec@sophia.inria.fr>
//
// ============================================================================
#ifndef CGAL_FPU_H
#define CGAL_FPU_H
// This file specifies some platform dependant functions, regarding the FPU
// directed rounding modes. There is only support for double precision.
// Some useful constants
#define CGAL_IA_MIN_DOUBLE (5e-324) // subnormal
#define CGAL_IA_MAX_DOUBLE (1.7976931348623157081e+308)
// Macro to stop compiler optimization.
#if defined(__GNUG__)
#define CGAL_IA_STOP_COMPILER_OPT(x) ({ volatile double y=(x);double z=y;z; })
#elif defined(_MSC_VER)
inline double cgal_ia_force_to_double(const double x)
{ volatile double e = x; return e; }
#define CGAL_IA_STOP_COMPILER_OPT(x) cgal_ia_force_to_double(x)
#endif
// The x87 keeps too wide exponents (15bits) in registers, even in double
// precision mode. This causes problems when the intervals overflow or
// underflow. To work around that, at every critical moment, we flush the
// register to memory, using the macro below.
// The other possible workaround is to use intervals of "long doubles"
// directly, but I think it would be much slower.
#if defined(__i386__) || defined(_MSC_VER)
#define CGAL_IA_FORCE_TO_DOUBLE(x) CGAL_IA_STOP_COMPILER_OPT(x)
#else
#define CGAL_IA_FORCE_TO_DOUBLE(x) (x)
#endif // __i386__
// We sometimes need to do the same thing to stop constant propagation.
#ifdef CGAL_IA_STOP_CONSTANT_PROPAGATION
#define CGAL_IA_STOP_CPROP(x) CGAL_IA_STOP_COMPILER_OPT(x)
#else
#define CGAL_IA_STOP_CPROP(x) (x)
#endif
#ifdef __linux__
#include <fpu_control.h>
#elif defined __SUNPRO_CC
#include <ieeefp.h>
#elif defined __osf || defined __osf__ || defined __BORLANDC__
#include <float.h>
#elif defined __sgi
// The 3 C functions do not work on IRIX 6.5 !!!!!
// So we use precompiled (by gcc) binaries linked into libCGAL.
// See revision 2.23 for the old code.
extern "C" {
void CGAL_workaround_IRIX_set_FPU_cw (int);
int CGAL_workaround_IRIX_get_FPU_cw (void);
}
#endif
CGAL_BEGIN_NAMESPACE
#ifdef __i386__
// The GNU libc version (cf powerpc) is nicer, but doesn't work on libc 5 :(
// This one also works with CygWin.
#define CGAL_IA_SETFPCW(CW) asm volatile ("fldcw %0" : :"m" (CW))
#define CGAL_IA_GETFPCW(CW) asm volatile ("fnstcw %0" : "=m" (CW))
typedef unsigned short FPU_CW_t;
enum {
FPU_cw_near = 0x000 | 0x127f,
FPU_cw_zero = 0xc00 | 0x127f,
FPU_cw_up = 0x800 | 0x127f,
FPU_cw_down = 0x400 | 0x127f
};
#elif defined __powerpc__
#define CGAL_IA_SETFPCW(CW) _FPU_SETCW(CW)
#define CGAL_IA_GETFPCW(CW) _FPU_GETCW(CW)
typedef fpu_control_t FPU_CW_t;
enum {
FPU_cw_near = _FPU_RC_NEAREST | _FPU_DEFAULT,
FPU_cw_zero = _FPU_RC_ZERO | _FPU_DEFAULT,
FPU_cw_up = _FPU_RC_UP | _FPU_DEFAULT,
FPU_cw_down = _FPU_RC_DOWN | _FPU_DEFAULT
};
#elif defined __SUNPRO_CC
#define CGAL_IA_GETFPCW(CW) CW = fpgetround()
#define CGAL_IA_SETFPCW(CW) fpsetround(fp_rnd(CW))
typedef unsigned int FPU_CW_t;
enum {
FPU_cw_near = FP_RN,
FPU_cw_zero = FP_RZ,
FPU_cw_up = FP_RP,
FPU_cw_down = FP_RM
};
#elif defined __sparc__
#define CGAL_IA_SETFPCW(CW) asm volatile ("ld %0,%%fsr" : :"m" (CW))
#define CGAL_IA_GETFPCW(CW) asm volatile ("st %%fsr,%0" : "=m" (CW))
typedef unsigned int FPU_CW_t;
enum { // rounding | precision | def.mask
FPU_cw_near = 0x0 | 0x20000000 | 0x1f,
FPU_cw_zero = 0x40000000 | 0x20000000 | 0x1f,
FPU_cw_up = 0x80000000 | 0x20000000 | 0x1f,
FPU_cw_down = 0xc0000000 | 0x20000000 | 0x1f
};
#elif defined __sgi
#define CGAL_IA_GETFPCW(CW) CW = CGAL_workaround_IRIX_get_FPU_cw()
#define CGAL_IA_SETFPCW(CW) CGAL_workaround_IRIX_set_FPU_cw(CW)
typedef unsigned int FPU_CW_t;
enum {
FPU_cw_near = 0x0,
FPU_cw_zero = 0x1,
FPU_cw_up = 0x2,
FPU_cw_down = 0x3
};
#elif defined __mips__ // && !defined __sgi
#define CGAL_IA_SETFPCW(CW) asm volatile ("ctc1 %0,$31" : :"r" (CW))
#define CGAL_IA_GETFPCW(CW) asm volatile ("cfc1 %0,$31" : "=r" (CW))
typedef unsigned int FPU_CW_t;
enum {
FPU_cw_near = 0x0,
FPU_cw_zero = 0x1,
FPU_cw_up = 0x2,
FPU_cw_down = 0x3
};
#elif defined __osf || defined __osf__ // Not yet supported.
#define CGAL_IA_GETFPCW(CW) CW = read_rnd()
#define CGAL_IA_SETFPCW(CW) write_rnd(CW)
typedef unsigned int FPU_CW_t;
enum {
FPU_cw_near = FP_RND_RN,
FPU_cw_zero = FP_RND_RZ,
FPU_cw_up = FP_RND_RP,
FPU_cw_down = FP_RND_RM
};
#elif defined __alpha__ // This one is not really supported [yet].
#define CGAL_IA_SETFPCW(CW) asm volatile ("mt_fpcr %0; excb" : :"f" (CW))
#define CGAL_IA_GETFPCW(CW) asm volatile ("excb; mf_fpcr %0" : "=f" (CW))
typedef unsigned long FPU_CW_t;
enum { // rounding
// I guess it won't work, because enum == int.
FPU_cw_near = 0x0800000000000000UL,
FPU_cw_zero = 0x0000000000000000UL,
FPU_cw_up = 0x0c00000000000000UL,
FPU_cw_down = 0x0400000000000000UL
};
#elif defined _MSC_VER
// Found in BIAS:
// #define CGAL_IA_SETFPCW(CW) _asm {fldcw word ptr ds:OFFSET CW}
// #define CGAL_IA_GETFPCW(CW) _asm {fstcw word ptr ds:OFFSET CW}
//
// Found in http://msdn.microsoft.com/library/sdkdoc/directx/imover_7410.htm :
#define CGAL_IA_SETFPCW(CW) __asm fldcw CW
#define CGAL_IA_GETFPCW(CW) __asm fstcw CW
typedef unsigned short FPU_CW_t;
enum {
FPU_cw_near = 0x0 | 0x127f,
FPU_cw_zero = 0xC00 | 0x127f,
FPU_cw_up = 0x800 | 0x127f,
FPU_cw_down = 0x400 | 0x127f
};
#elif defined __BORLANDC__
#define CGAL_IA_SETFPCW(CW) _control87(CW,~0)
#define CGAL_IA_GETFPCW(CW) CW = _control87(0,0)
typedef unsigned short FPU_CW_t;
enum {
FPU_cw_near = 0x0 | 0x127f,
FPU_cw_zero = 0xC00 | 0x127f,
FPU_cw_up = 0x800 | 0x127f,
FPU_cw_down = 0x400 | 0x127f
};
#else
#error Architecture not supported
#endif
// User interface:
inline
FPU_CW_t
FPU_get_cw (void)
{
FPU_CW_t cw;
CGAL_IA_GETFPCW(cw);
return cw;
}
inline
void
FPU_set_cw (FPU_CW_t cw)
{
CGAL_IA_SETFPCW(cw);
}
inline
FPU_CW_t
FPU_get_and_set_cw (FPU_CW_t cw)
{
FPU_CW_t old = FPU_get_cw();
FPU_set_cw(cw);
return old;
}
FPU_CW_t FPU_empiric_test(); // Only used for debug.
CGAL_END_NAMESPACE
#endif // CGAL_FPU_H