如何使用优化级别防止C99浮点代码更改结果
我在下面编写此代码是为了测试系统是否符合C99的IEEE 754。问题是结果会随着优化级别的不同而变化。如果我坚持使用优化级别0,那么大多数测试都会通过。但是如果我将优化级别增加到-O3,那么大多数测试都会失败。我在一家商店里试过。它没有改变任何结果 我已经在Mac OS 10.4.11 PowerPC上使用GCC 5.2.0尝试过这个程序,在Mac OS 10.6.8上使用GCC 4.2.1和GCC 4.9.2尝试过这个程序。结果是一样的。大多数测试只有在使用优化级别0(-O0)时才通过。我认为这是GCC的一个bug,但在我报告它之前,我想听听其他人对代码的看法 这就是我编译代码的方式:gcc-o testc99 main.c-O0如何使用优化级别防止C99浮点代码更改结果,c,gcc,optimization,c99,ieee,C,Gcc,Optimization,C99,Ieee,我在下面编写此代码是为了测试系统是否符合C99的IEEE 754。问题是结果会随着优化级别的不同而变化。如果我坚持使用优化级别0,那么大多数测试都会通过。但是如果我将优化级别增加到-O3,那么大多数测试都会失败。我在一家商店里试过。它没有改变任何结果 我已经在Mac OS 10.4.11 PowerPC上使用GCC 5.2.0尝试过这个程序,在Mac OS 10.6.8上使用GCC 4.2.1和GCC 4.9.2尝试过这个程序。结果是一样的。大多数测试只有在使用优化级别0(-O0)时才通过。我认
// FILE: main.c
// Description: Tests C99 IEEE 754 compliance.
// Note: use -O0 when compiling to make most of the tests pass
// Date: 11-24-2016
#include <stdio.h>
#include <math.h>
#include <inttypes.h>
#include <fenv.h>
#include <float.h>
#pragma STDC FENV_ACCESS ON
// Used to convert unsigned integer <--> double
union Converter
{
double d;
uint64_t i;
};
typedef union Converter Converter;
#pragma STDC FENV_ACCESS on
int check_for_exceptions(void)
{
// Set to 1 to enable debug printing
if (0) {
if (fetestexcept(FE_DIVBYZERO)) {
printf("FE_DIVBYZERO detected\n");
}
if (fetestexcept(FE_INEXACT)) {
printf("FE_INEXACT detected\n");
}
if (fetestexcept(FE_INVALID)) {
printf("FE_INVALID detected\n");
}
if (fetestexcept(FE_OVERFLOW)) {
printf("FE_OVERFLOW detected\n");
}
if (fetestexcept(FE_UNDERFLOW)) {
printf("FE_UNDERFLOW detected\n");
}
}
return fetestexcept(FE_ALL_EXCEPT);
}
// Adds two really big numbers together in order to cause an overflow
void test_overflow(void)
{
double answer, num1, num2;
num1 = 1.7 * pow(10, 308); // the very limits of IEEE 754 double precision numbers
num2 = num1;
feclearexcept(FE_ALL_EXCEPT);
// adding two really big numbers together should trigger an overflow
answer = num1 + num2;
printf("Test overflow...");
if (check_for_exceptions() == (FE_OVERFLOW | FE_INEXACT)) {
printf("pass\n");
} else {
printf("fail\n");
}
}
void test_underflow(void)
{
double answer;
feclearexcept(FE_ALL_EXCEPT);
//answer = DBL_MIN / 3000000000000000.0; // does not produce an exception
answer = fma(DBL_MIN, 1.0/10.0, 0); // Inexact and underflow exceptions produced
printf("Test underflow...");
if (check_for_exceptions() == (FE_UNDERFLOW | FE_INEXACT)) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// Test if the inexact exception can be produced under the right conditions
void test_inexact(void)
{
double answer;
feclearexcept(FE_ALL_EXCEPT);
answer = log(1.1);
printf("Test inexact...");
if (check_for_exceptions() == FE_INEXACT) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// test to see if the invalid exception works
void test_invalid(void)
{
double d;
feclearexcept(FE_ALL_EXCEPT);
d = sqrt(-1.0);
printf("Test invalid...");
if (check_for_exceptions() == FE_INVALID) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// test the fused multiply-add operation
void test_fma(void)
{
double result, correct_answer, num1, num2, num3;
int iteration, max_iterations;
result = 0.0;
correct_answer = -13819435189200605973481192570224640.0;
num1 = 5.2;
num2 = 1.3 * pow(10, 18);
num3 = -3.0;
max_iterations = pow(10, 7);
feclearexcept(FE_ALL_EXCEPT);
// Test large number of multiplication, addition, and subtraction calculations
for (iteration = 0; iteration < max_iterations; iteration++) {
result += fma(num1, num2, num3);
num1 += 1.0000002;
num2 -= 1.3044 * pow(10,14);
num3 += 0.953343;
}
// Test division - or multiplication with the reciprical
result = fma(result, 1.0/3.14159265, -987654321.123456789);
printf("Test fma()...");
if (result == correct_answer) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// Test what happens with infinity - infinity
void test_inf_minus_inf()
{
double answer;
//answer = INFINITY - INFINITY; // does not cause an exception, but does make a nan
feclearexcept(FE_ALL_EXCEPT);
answer = fma(INFINITY, 1, -INFINITY); // does cause an invalid exception, answer is nan. -INFINITY - INFINITY doesn't cause an exception
printf("Testing infinity - infinity...");
if (check_for_exceptions() == FE_INVALID) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// Test signalling nan - should produce an invalid exception
void test_snan()
{
double result;
Converter c;
c.i = 0x7FF0000000000001;
feclearexcept(FE_ALL_EXCEPT);
result = fma(c.d, 10.4, 0.11);
printf("Test snan...");
if (check_for_exceptions() == FE_INVALID) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// Test quiet nan - no exceptions should be produced
void test_qnan()
{
Converter c;
double result;
c.i = 0x7fffffff;
feclearexcept(FE_ALL_EXCEPT);
result = fma(c.d, 10.4, 0.11);
printf("Test qnan...");
if (check_for_exceptions() == 0) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// Test infinity * zero for invalid exception
void test_inf_times_zero()
{
double answer;
//answer = INFINITY * 0; // answer is nan, no exception
feclearexcept(FE_ALL_EXCEPT);
answer = fma(INFINITY, 0, 0); // answer is nan, invalid exception raised
printf("Test infinity * 0...");
if (check_for_exceptions() == FE_INVALID) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// Test division by zero exception
void test_one_divided_by_zero()
{
double answer;
feclearexcept(FE_ALL_EXCEPT);
answer = fma(1.0, 1.0/0.0, 0.0); // division by zero exception, answer = inf
//answer = 1.0/0.0; // division by zero exception, answer = inf
printf("Test division by zero...");
if (check_for_exceptions() == FE_DIVBYZERO) {
printf("pass\n");
} else {
printf("fail\n");
}
}
// verify all rounding modes work correctly
void test_rounding_modes(void)
{
double result, expected_result;
printf("Test rounding...");
do {
fesetround(FE_TONEAREST);
expected_result = 2.0;
result = rint(2.1);
if (result != expected_result) {
break;
}
fesetround(FE_UPWARD);
expected_result = 3.0;
result = rint(2.1);
if (result != expected_result) {
break;
}
fesetround(FE_DOWNWARD);
expected_result = 2.0;
result = rint(2.1);
if (result != expected_result) {
break;
}
fesetround(FE_TOWARDZERO);
expected_result = 2.0;
result = rint(2.1);
if (result != expected_result) {
break;
}
printf("pass\n");
return;
} while (0);
printf("fail\n");
}
// Test square root results
void test_sqrt(void)
{
double answer, result, base, expected_result;
base = 8.123456;
answer = pow(base, 2);
result = sqrt(answer);
expected_result = 8.1234559999999973;
printf("Test sqrt...");
if (result == expected_result) {
printf("pass\n");
} else {
printf("fail\n");
}
}
int main (int argc, const char * argv[]) {
test_inf_minus_inf();
test_inf_times_zero();
test_one_divided_by_zero();
test_fma();
test_overflow();
test_underflow();
test_inexact();
test_invalid();
test_snan();
test_qnan();
test_rounding_modes();
test_sqrt();
return 0;
}
//文件:main.c
//描述:测试C99与IEEE 754的符合性。
//注意:编译时使用-O0使大多数测试通过
//日期:2016年11月24日
#包括
#包括
#包括
#包括
#包括
#布拉格STDC FENV_通道
//用于转换无符号整数双精度
联合变流器
{
双d;
uint64_t i;
};
DEF型活接头转换器;
#布拉格STDC FENV_通道
int check_是否存在异常(无效)
{
//设置为1以启用调试打印
如果(0){
if(fetestexcept(feu除以零)){
printf(“检测到FE_DIVBYZERO\n”);
}
if(异常(FE_不精确)){
printf(“检测到FE_不精确\n”);
}
如果(fetestexcept(FE_无效)){
printf(“检测到FE_无效\n”);
}
if(fetestexcept(FE_溢出)){
printf(“检测到FE_溢出\n”);
}
if(fetestexcept(FE_下溢)){
printf(“检测到FE_下溢\n”);
}
}
返回fetestexcept(FE_所有_除外);
}
//将两个非常大的数字相加以导致溢出
无效测试_溢出(无效)
{
双答案,num1,num2;
num1=1.7*pow(10308);//IEEE 754双精度数字的极限
num2=num1;
FeClearException(FE_ALL_除外);
//将两个非常大的数字相加应该会触发溢出
答案=num1+num2;
printf(“测试溢出…”);
如果(检查是否存在异常()=(FE_溢出| FE_不精确)){
printf(“pass\n”);
}否则{
printf(“失败\n”);
}
}
孔隙测试\u底流(孔隙)
{
双重回答;
FeClearException(FE_ALL_除外);
//answer=DBL_MIN/30000000000000000.0;//不产生异常
答案=fma(DBL_MIN,1.0/10.0,0);//产生不精确和下溢异常
printf(“测试下溢…”);
如果(检查是否存在异常()=(FE_下溢| FE_不精确)){
printf(“pass\n”);
}否则{
printf(“失败\n”);
}
}
//测试在正确的条件下是否可以产生不精确异常
无效测试不精确(无效)
{
双重回答;
FeClearException(FE_ALL_除外);
答案=对数(1.1);
printf(“测试不精确…”);
如果(检查是否存在异常()==FE\U不精确){
printf(“pass\n”);
}否则{
printf(“失败\n”);
}
}
//测试以查看无效异常是否有效
无效测试_无效(无效)
{
双d;
FeClearException(FE_ALL_除外);
d=sqrt(-1.0);
printf(“测试无效…”);
如果(检查是否存在异常()==FE\U无效){
printf(“pass\n”);
}否则{
printf(“失败\n”);
}
}
//测试融合乘法加法操作
失效测试(失效)
{
双重结果,正确答案,num1,num2,num3;
int迭代,max_迭代;
结果=0.0;
正确答案=-13819435189200605973481192570224640.0;
num1=5.2;
num2=1.3*pow(10,18);
num3=-3.0;
最大迭代次数=功率(10,7);
FeClearException(FE_ALL_除外);
//测试大量乘法、加法和减法计算
对于(迭代=0;迭代