C++ Builder 串口控件 | C++ Builder 编程技巧 | C++ Builder 操作指南 | C++ Builder 参考手册 | 基础知识 | cfloat 浮点数 | • 浮点数类型 | • 浮点数异常处理 | • _finite, _finitel | • _isinf, _isinfl | • _isnan, _isnanl | • _fpclass, _fpclassl | • _chgsign, _chgsignl | • _copysign, _copysignl | • _logb, _logbl | • _scalb, _scalbl | • _nextafter, _nextafterl | • _clear87, _clearfp | • _control87, _controlfp | • _status87, _statusfp | • _fpreset | cmath 数学函数 | cstdlib 标准库函数 | System 字符串 | System 日期和时间 | System.Math.hpp 数学函数 | 其他数据类型 | VCL 基础类 | VCL 应用程序 | Pictures 图片 | Graphics 绘图 | Additional 控件 | System 控件 | A ~ Z 字母顺序排列的目录 | 网友留言/技术支持 |
|
_control87, _controlfp - 改变 x87 (x86 的浮点内核) 的数据处理方式,或屏蔽和启用浮点异常 |
_control87, _controlfp:改变 x87 (x86 的浮点内核) 的数据处理方式,或屏蔽和启用浮点异常
函数原型:
unsigned int _control87(unsigned int newcw, unsigned int mask); |
#define _controlfp(a, b) _control87((a), ((b)& (~EM_DENORMAL))) |
头文件:
#include <cfloat>
命名空间:
std
参数:
newcw:新的控制字
mask:控制字掩码,只有掩码等于 1 的二进制位对应的新控制字二进制位写入 x87 控制字
异常中断控制 |
|
|
MCW_EM |
0x003f |
异常中断掩码 (interrupt Exception Masks),
控制字的这些位如果等于 0 产生对应的异常中断 (抛出异常) 或等于 1 得到 ± 0、± NAN、± INF 等计算结果 |
EM_INVALID |
0x0001 |
不合理的运算 (invalid),例如 0.0 除以 0.0 就是不合理的运算,无法计算出结果。
控制字这一位等于 0:抛出异常 EInvalidOp;等于 1:计算结果为 ± NAN |
EM_DENORMAL |
0x0002 |
次正常 (denormal):有效数字向下溢出一部分,可以使用精度不足的浮点数来表示运算结果【注】
控制字这一位等于 0:抛出异常 EInvalidOp;等于 1:计算结果用 “次正常” 的浮点数表示 |
EM_ZERODIVIDE |
0x0004 |
被零除 (zero divide),不等于零的数值除以零。
控制字这一位等于 0:抛出异常 EZeroDivide;等于 1:计算结果为 ± INF |
EM_OVERFLOW |
0x0008 |
向上溢出 (overflow),绝对值太大,超过了浮点数能够表达的范围【注】
控制字这一位等于 0:抛出异常 EOverflow;等于 1:计算结果等于 ± HUGE_VAL |
EM_UNDERFLOW |
0x0010 |
向下溢出 (underflow),绝对值太小,超过了浮点数能够表达的范围【注】
控制字这一位等于 0:抛出异常 EUnderflow;等于 1:计算结果等于 ± 0 |
EM_INEXACT |
0x0020 |
降低精度 (inexact (precision)),例如把 double 赋值给 float,降低了精度【注】
控制字这一位等于 0:抛出异常 EInvalidOp;等于 1:结果为降低精度的值 |
无穷大控制 |
|
|
MCW_IC |
0x1000 |
无穷大控制掩码 (Infinity Control) |
IC_AFFINE |
0x1000 |
仿射 (affine) 新的 x87 (从 387 开始) 采取仿射无穷大,即使用负无穷大和正无穷大 |
IC_PROJECTIVE |
0x0000 |
投影 (projective) 早期的浮点数内核 287 使用投影无穷大,即无符号无穷大,正无穷大和负无穷大相等 |
舍入控制 |
|
|
MCW_RC |
0x0c00 |
舍入控制掩码 (Rounding Control) |
RC_CHOP |
0x0c00 |
舍弃 (chop) |
RC_UP |
0x0800 |
向上 (up) |
RC_DOWN |
0x0400 |
向下 (down) |
RC_NEAR |
0x0000 |
接近 (near),现在的 x87 默认采用接近值,即四舍五入 |
精度控制 |
|
|
MCW_PC |
0x0300 |
精度控制掩码 (Precision Control) |
PC_24 |
0x0000 |
24 bits |
PC_53 |
0x0200 |
53 bits |
PC_64 |
0x0300 |
64 bits,现在的 x87 默认采用 64 bits 精度 |
注:溢出、向上溢出、向下溢出、次正常、降低精度
浮点数向上和向下超出范围是有区别的:
• 向上 (向左方向) 只要有一位超过范围了,是最高位丢失,数据完全失去意义了,就是向上溢出 (overflow)
• 向下 (向右方向) 如果有超过范围的数据,是低位丢失,相当于降低了精度,属于次正常 (denormal) 数据,一直到最高位也丢失了,数据才失去意义,这时候才算是向下溢出 (underflow)
• 在需要保证精度的情况下,次正常就属于错误数据了,如果精度要求不高的情况下,次正常数据还可以用,所以可以设定次正常是否抛出异常
最大值 |
|
|
9 |
9 |
9 |
9 |
9 |
9 |
0 |
0 |
0 |
0 |
0 |
0 |
. |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
假定浮点数表示的最大值 |
最小值 |
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
. |
0 |
0 |
0 |
0 |
0 |
0 |
9 |
9 |
9 |
9 |
9 |
9 |
|
|
|
|
|
|
|
假定浮点数表示的最小值 |
向上溢出 |
|
1 |
2 |
3 |
4 |
5 |
6 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
. |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
最高位丢失,数据的值失去意义,向上溢出 |
正常值 |
|
|
1 |
2 |
3 |
4 |
5 |
6 |
0 |
0 |
0 |
0 |
0 |
0 |
. |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
保证精度的正常值 |
正常值 |
|
|
|
|
|
|
|
|
|
|
1 |
2 |
3 |
4 |
. |
5 |
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
保证精度的正常值 |
降低精度 |
|
|
|
|
|
|
|
|
|
|
1 |
2 |
3 |
4 |
. |
5 |
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
正常值范围内丢弃低位,例如双精度值赋值给单精度 |
正常值 |
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
. |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
|
|
|
|
|
|
|
保证精度的正常值 |
次正常 |
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
. |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
|
|
|
|
|
|
低位丢失,降低了精度,如果精度要求不高还可以用 |
次正常 |
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
. |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
|
|
低位丢失,降低了精度,如果精度要求不高还可以用 |
向下溢出 |
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
. |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
|
最高位丢失,数据的值失去意义,向下溢出 |
返回值:
新的浮点数控制字
例1:次正常 (denormal) 抛出异常的例子
void __fastcall TForm1::Button1Click(TObject *Sender)
{
UnicodeString s;
try
{
// 保存原来的控制字,新控制字为次正常抛出异常
unsigned int uOldC87 = std::_control87(~EM_DENORMAL, MCW_EM);
try
{
double x = 2.1e-308;
double y = x / 2.0;
s.cat_sprintf(L"%g\r\n", y);
}
__finally
{
std::_control87(uOldC87, MCW_EM); // 恢复原来的控制字
}
}
catch(Exception &E)
{
s += L"捕获异常: " + E.ClassName() + L"\r\n";
s += L"错误信息: " + E.Message + L"\r\n";
}
Memo1->Lines->Text = s;
} |
例2:次正常 (denormal) 不抛出异常,得到精度不足的值的例子
void __fastcall TForm1::Button1Click(TObject *Sender)
{
UnicodeString s;
try
{
// 保存原来的控制字,新控制字为次正常得到精度不足的计算结果
unsigned int uOldC87 = std::_control87(MCW_EM, MCW_EM);
try
{
double x = 2.1e-308;
double y = x / 2.0;
s.cat_sprintf(L"%g\r\n", y);
}
__finally
{
std::_control87(uOldC87, MCW_EM); // 恢复原来的控制字
}
}
catch(Exception &E)
{
s += L"捕获异常: " + E.ClassName() + L"\r\n";
s += L"错误信息: " + E.Message + L"\r\n";
}
Memo1->Lines->Text = s;
} |
兼容性:
clang64 不支持这一组函数,其中:
• “异常中断控制” 可以使用 GetExceptionMask 和 SetExceptionMask 函数;
• “舍入控制” 可以使用 GetRoundMode 和 SetRoundMode 函数;
• “精度控制” 可以使用 GetPrecisionMode 和 SetPrecisionMode 函数。
相关链接:
• _finite • _isinf • _isnan • _fpclass • _control87 • SetExceptionMask • _matherr • 浮点数异常处理
|
|
|
|