fpu.C
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifndef UTIL_FPU_C_DEFINED
00039 #define UTIL_FPU_C_DEFINED
00040
00041 #include "Util/fpu.H"
00042
00043 #include "Util/StringConversions.H"
00044 #include "Util/log.H"
00045
00046 #include <cstdio>
00047
00048 namespace
00049 {
00050 #ifdef INVT_CPU_IX86
00051
00052 const unsigned short infmask = (1<<12);
00053 const unsigned short roundmask = ((1<<11)|(1<<10));
00054 const unsigned short precmask = ((1<<9)|(1<<8));
00055 const unsigned short exceptmask = 0xff;
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 unsigned short get_x87_control_word()
00086 {
00087 volatile unsigned short ctrl;
00088
00089 asm("fstcw %0"
00090 :"=m"(ctrl));
00091
00092 return ctrl;
00093 }
00094
00095 void set_x87_control_word(volatile unsigned short ctrl)
00096 {
00097 asm("fldcw %0\n\t"
00098 "fwait"
00099 :
00100 :"m"(ctrl));
00101 }
00102
00103 void show_x87_control_word(unsigned short ctrl)
00104 {
00105 printf("control word: 0x%04x\n", int(ctrl));
00106 printf(" infinity-control bits: 0x%04x\n", int(ctrl & infmask));
00107 printf(" rounding-control bits: 0x%04x\n", int(ctrl & roundmask));
00108 printf(" precision-control bits: 0x%04x\n", int(ctrl & precmask));
00109 printf(" interrupt masks: 0x%04x\n", int(ctrl & exceptmask));
00110 }
00111 #endif
00112 }
00113
00114
00115 FpuPrecision getFpuPrecision()
00116 {
00117 #ifdef INVT_CPU_IX86
00118 const unsigned short ctrl = get_x87_control_word();
00119 const unsigned short precbits = (ctrl & ((1<<9)|(1<<8))) >> 8;
00120 switch (precbits)
00121 {
00122 case 0: return FPU_PREC_SINGLE;
00123 case 2: return FPU_PREC_DOUBLE;
00124 case 3: return FPU_PREC_EXTENDED;
00125 }
00126 LINFO("bogus x87 precision control bits '%d'", precbits);
00127 return FPU_PREC_EXTENDED;
00128 #else
00129 LDEBUG("warning: x87 control word not available; "
00130 "assuming extended precision");
00131 return FPU_PREC_EXTENDED;
00132 #endif
00133 }
00134
00135
00136 void setFpuPrecision(FpuPrecision prec)
00137 {
00138 #ifdef INVT_CPU_IX86
00139 volatile unsigned short ctrl = get_x87_control_word();
00140 switch (prec)
00141 {
00142 case FPU_PREC_SINGLE:
00143 ctrl &= (~(1<<8));
00144 ctrl &= (~(1<<9));
00145 set_x87_control_word(ctrl);
00146 break;
00147 case FPU_PREC_DOUBLE:
00148 ctrl &= (~(1<<8));
00149 ctrl |= (1<<9);
00150 set_x87_control_word(ctrl);
00151 break;
00152 case FPU_PREC_EXTENDED:
00153 ctrl |= (1<<8);
00154 ctrl |= (1<<9);
00155 set_x87_control_word(ctrl);
00156 break;
00157 }
00158 #else
00159 if (prec != FPU_PREC_EXTENDED)
00160 LERROR("warning: x87 control word not available; "
00161 "attempt to set precision to non-default value '%s' ignored",
00162 convertToString(prec).c_str());
00163 #endif
00164 }
00165
00166
00167 std::string convertToString(const FpuPrecision& val)
00168 {
00169 switch (val)
00170 {
00171 case FPU_PREC_SINGLE: return "single";
00172 case FPU_PREC_DOUBLE: return "double";
00173 case FPU_PREC_EXTENDED: return "extended";
00174 }
00175 LFATAL("unknown FpuPrecision enumerant '%d'", int(val));
00176 return std::string();
00177 }
00178
00179
00180 void convertFromString(const std::string& str, FpuPrecision& val)
00181 {
00182 if (str.compare("single") == 0) { val = FPU_PREC_SINGLE; }
00183 else if (str.compare("double") == 0) { val = FPU_PREC_DOUBLE; }
00184 else if (str.compare("extended") == 0) { val = FPU_PREC_EXTENDED; }
00185 else
00186 conversion_error::raise<FpuPrecision>(str);
00187 }
00188
00189
00190 FpuRoundingMode getFpuRoundingMode()
00191 {
00192 #ifdef INVT_CPU_IX86
00193 const unsigned short ctrl = get_x87_control_word();
00194 const unsigned short roundbits = (ctrl & ((1<<11)|(1<<10))) >> 10;
00195 switch (roundbits)
00196 {
00197 case 0: return FPU_ROUND_NEAR;
00198 case 1: return FPU_ROUND_DOWN;
00199 case 2: return FPU_ROUND_UP;
00200 case 3: return FPU_ROUND_ZERO;
00201 }
00202 LINFO("bogus x87 precision control bits '%d'", roundbits);
00203 return FPU_ROUND_NEAR;
00204 #else
00205 LDEBUG("warning: x87 control word not available; "
00206 "assuming round-nearest");
00207 return FPU_ROUND_NEAR;
00208 #endif
00209 }
00210
00211
00212 void setFpuRoundingMode(FpuRoundingMode roundmode)
00213 {
00214 #ifdef INVT_CPU_IX86
00215 volatile unsigned short ctrl = get_x87_control_word();
00216 switch (roundmode)
00217 {
00218 case FPU_ROUND_NEAR:
00219 ctrl &= (~(1<<11));
00220 ctrl &= (~(1<<10));
00221 set_x87_control_word(ctrl);
00222 break;
00223 case FPU_ROUND_DOWN:
00224 ctrl &= (~(1<<11));
00225 ctrl |= (1<<10);
00226 set_x87_control_word(ctrl);
00227 break;
00228 case FPU_ROUND_UP:
00229 ctrl |= (1<<11);
00230 ctrl &= (~(1<<10));
00231 set_x87_control_word(ctrl);
00232 break;
00233 case FPU_ROUND_ZERO:
00234 ctrl |= (1<<11);
00235 ctrl |= (1<<10);
00236 set_x87_control_word(ctrl);
00237 break;
00238 }
00239 #else
00240 if (roundmode != FPU_ROUND_NEAR)
00241 LERROR("warning: x87 control word not available; "
00242 "attempt to set rounding mode to non-default value '%s' ignored",
00243 convertToString(roundmode).c_str());
00244 #endif
00245 }
00246
00247
00248 std::string convertToString(const FpuRoundingMode& val)
00249 {
00250 switch (val)
00251 {
00252 case FPU_ROUND_NEAR: return "nearest";
00253 case FPU_ROUND_DOWN: return "down";
00254 case FPU_ROUND_UP: return "up";
00255 case FPU_ROUND_ZERO: return "zero";
00256 }
00257 LFATAL("unknown FpuRoundingMode enumerant '%d'", int(val));
00258 return std::string();
00259 }
00260
00261
00262 void convertFromString(const std::string& str, FpuRoundingMode& val)
00263 {
00264 if (str.compare("nearest") == 0) { val = FPU_ROUND_NEAR; }
00265 else if (str.compare("down") == 0) { val = FPU_ROUND_DOWN; }
00266 else if (str.compare("up") == 0) { val = FPU_ROUND_UP; }
00267 else if (str.compare("zero") == 0) { val = FPU_ROUND_ZERO; }
00268 else
00269 conversion_error::raise<FpuRoundingMode>(str);
00270 }
00271
00272
00273
00274
00275
00276
00277
00278 #endif // UTIL_FPU_C_DEFINED