英特尔编译器无法处理小数的绝对值
Intel compilers cannot handle absolute value of small number
与 gcc 7.2.0
相比,使用 intel 2018
编译我的代码时,我遇到了一些非常奇怪的舍入错误。我只是在考虑获取极小数字的绝对值:
#include <cfloat>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
double numa = -1.3654159537789158e-08;
double numb = -7.0949094162313382e-08;
if (isnan(numa))
printf("numa is nan \n");
if (isnan(numb))
printf("numb is nan \n");
printf("abs(numa) %.17g \n", abs(numa));
printf("abs(numb) %.17g \n", abs(numb));
if ((isnan(numa) || (abs(numa) < DBL_EPSILON)) || (isnan(numb) || (abs(numb) < DBL_EPSILON))) {
printf("x %.17g y %.17g DBL_E %.17g \n", numa, numb, DBL_EPSILON);
}
return 0;
}
这是使用 gcc 7.2.0
编译代码时的预期输出:
$ ./a.out
abs(numa) 1.3654159537789158e-08
abs(numb) 7.0949094162313382e-08
但 intel/2018
则不同:
$ ./a.out
abs(numa) 2.0410903428666442e-314
abs(numb) 2.0410903428666442e-314
x -1.3654159537789158e-08 y -7.0949094162313382e-08 DBL_E 2.2204460492503131e-16
是什么导致我的英特尔编译器版本有如此巨大的差异?
错误的功能或错误的语言
"gcc 7.2.0" 的输出符合预期,因为 OP 是用 C++ 编译的
"intel/2018" 输出与强制 C 编译一致。
对于 C,abs(numa)
将 numa
转换为值为 0 的 int
,下面是 未定义的行为(UB ) 因为 "%.17g"
期望 double
而不是 int
.
// In C UB: vvvvv------vvvvvvvvv
printf("abs(numa) %.17g \n", abs(numa));
利用 "abs(numa) 2.0410903428666442e-314"
的 UB 输出,我们可以做一些取证。
典型的2.0410903428666442e-314在binary是
00000000 00000000 00000000 00000000 11110110 00111101 01001110 00101110
这与某些通过 32 位 int 0
然后 printf()
检索它以及预期的其他后续垃圾的 C 编译是一致的 double
.
作为 UB,此结果可能会不时变化,如果有输出的话,但它是问题的良好指标:用 C++ 编译或更改为 fabs()
()在 C++ 和 C 中取 double
的绝对值。
OP 在调试浮点问题时使用 "%g"
(或 "%e"
)的一些荣誉。 "%f"
的信息量更大
与 gcc 7.2.0
相比,使用 intel 2018
编译我的代码时,我遇到了一些非常奇怪的舍入错误。我只是在考虑获取极小数字的绝对值:
#include <cfloat>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
double numa = -1.3654159537789158e-08;
double numb = -7.0949094162313382e-08;
if (isnan(numa))
printf("numa is nan \n");
if (isnan(numb))
printf("numb is nan \n");
printf("abs(numa) %.17g \n", abs(numa));
printf("abs(numb) %.17g \n", abs(numb));
if ((isnan(numa) || (abs(numa) < DBL_EPSILON)) || (isnan(numb) || (abs(numb) < DBL_EPSILON))) {
printf("x %.17g y %.17g DBL_E %.17g \n", numa, numb, DBL_EPSILON);
}
return 0;
}
这是使用 gcc 7.2.0
编译代码时的预期输出:
$ ./a.out
abs(numa) 1.3654159537789158e-08
abs(numb) 7.0949094162313382e-08
但 intel/2018
则不同:
$ ./a.out
abs(numa) 2.0410903428666442e-314
abs(numb) 2.0410903428666442e-314
x -1.3654159537789158e-08 y -7.0949094162313382e-08 DBL_E 2.2204460492503131e-16
是什么导致我的英特尔编译器版本有如此巨大的差异?
错误的功能或错误的语言
"gcc 7.2.0" 的输出符合预期,因为 OP 是用 C++ 编译的
"intel/2018" 输出与强制 C 编译一致。
对于 C,abs(numa)
将 numa
转换为值为 0 的 int
,下面是 未定义的行为(UB ) 因为 "%.17g"
期望 double
而不是 int
.
// In C UB: vvvvv------vvvvvvvvv
printf("abs(numa) %.17g \n", abs(numa));
利用 "abs(numa) 2.0410903428666442e-314"
的 UB 输出,我们可以做一些取证。
典型的2.0410903428666442e-314在binary是
00000000 00000000 00000000 00000000 11110110 00111101 01001110 00101110
这与某些通过 32 位 int 0
然后 printf()
检索它以及预期的其他后续垃圾的 C 编译是一致的 double
.
作为 UB,此结果可能会不时变化,如果有输出的话,但它是问题的良好指标:用 C++ 编译或更改为 fabs()
(double
的绝对值。
OP 在调试浮点问题时使用 "%g"
(或 "%e"
)的一些荣誉。 "%f"