为了避免链接libmath,我编写了我自己需要的两个函数。这不是一个大问题,但它在魔鬼引诱的细节中:现在我有问题,我需要定义和测试NaN和+/- Infinity没有bit-juggling(不能直接设置位,需要< em> completly endian-agnostic)。也没有那么大的问题,因为所有的现代编译器都支持IEEE-754,或者它们不支持?他们不。当然,他们没有。至少不是以一致和可测试的方式。如果他们这样做,你可以关闭它。有时他们会定义一个宏,有时候不会,有时候我不知道,这就是我的咆哮结束而我的问题开始的地方:
哪些编译器支持构建和测试NaN和NaN的方法 以下列方式+/-无限。
#include <stdio.h>
#include <stdlib.h>
#ifdef FALSEONLY
# define PUTS(x) // noop
#else
# define PUTS(x) puts(x)
#endif
#define DOUBLE_MAX 1.7976931348623158e308
int main()
{
double positive_infinity, negative_infinity, nan;
int num_errors;
/*
* Some compilers need that workaround.
*
* Known for such behaviour are Sun C 5.0, Compaq (formerly DEC) 6.4 and MSVC 9
* https://lists.gnu.org/archive/html/bug-gnulib/2007-03/msg00360.html
* https://github.com/rofl0r/gnulib/blob/master/tests/nan.h
*
* There are compilers which do not know a NaN like the
* C2000 CLA C Compiler (Texas Instruments). But I have older data only (from 2013),
* that behaviour might have changed in the meantime.
* https://github.com/cpputest/cpputest/issues/132
*/
// #if defined __SUNPRO_C || defined __DECC || defined _MSC_VER
#ifdef USE_ZERO
double zero;
zero = 0.0;
positive_infinity = 1.0 / zero;
negative_infinity = -1.0 / zero;
nan = zero / zero;
#else
/*
* "For division, when the divisor is zero and the dividend is a finite
* non-zero number, the sign of the infinity is the exclusive OR of
* the operands’ signs"
*
* -- IEEE-754 2008 7.3 Division by zero
*/
positive_infinity = 1.0 / 0.0;
negative_infinity = -1.0 / 0.0;
/*
* "For operations producing results in floating-point format, the default
* result of an operation that signals the invalid operation exception
* shall be a quiet NaN that should provide some diagnostic information (see 6.2).
* These operations are:
* [...]
* e) division: division(0, 0) or division(∞, ∞)
* [...]"
*
* -- IEEE-754 2008 7.2(e) Invalid operation
*/
nan = 0.0 / 0.0;
#endif
num_errors = 0;
/*
* " Every NaN shall compare unordered with everything, including itself."
*
* -- IEEE-754 2008 5.11 Details of comparison predicates
*
*
* Does not work with MSVC and GCC if the option to do "fast math" is
* switched on.
*
* GCC defines __FAST_MATH__ in that case.
*
* How to check for it with MSVC?
*
* What's with Intel compilers? The author doesn't have seven hundred bucks to burn
* and cannot sign the license agreement for the "free" version with a clear conscience.
*
* At least icpc 12.1.4 seems to work.
* http://rosettacode.org/wiki/Extreme_floating_point_values#C
*/
if (nan != nan) {
PUTS("TRUE: nan != nan");
} else {
num_errors++;
puts("FALSE: nan != nan");
}
#ifdef USE_ZERO
if (nan == 0.0 / zero) {
num_errors++;
PUTS("TRUE: nan == 0.0/0.0 (WRONG acc. to IEEE-754)");
} else {
puts("FALSE: nan == 0.0/0.0 (CORRECT acc. to IEEE-754)");
}
#else
if (nan == 0.0 / 0.0) {
num_errors++;
PUTS("TRUE: nan == 0.0/0.0 (WRONG acc. to IEEE-754)");
} else {
puts("FALSE: nan == 0.0/0.0 (CORRECT acc. to IEEE-754)");
}
#endif
/*
* Outcome depends on rounding mode, see "IEEE-754 2008 7.4 Overflow" for details.
* Must both hold if the rounding mode is either roundTiesToEven or roundTiesToAway.
*/
if (positive_infinity == 2 * DOUBLE_MAX) {
PUTS("TRUE: positive_infinity == 2 * DOUBLE_MAX");
} else {
num_errors++;
puts("FALSE: positive_infinity == 2 * DOUBLE_MAX");
}
if (negative_infinity == -(2 * DOUBLE_MAX)) {
PUTS("TRUE: negative_infinity == -(2 * DOUBLE_MAX)");
} else {
num_errors++;
puts("FALSE: negative_infinity == -(2 * DOUBLE_MAX)");
}
/*
* "Infinities shall be interpreted in the affine sense, that is:
* −∞ < {every finite number} < +∞"
*
* -- IEEE-754 2008 6.1 Infinity arithmetic
*/
if (negative_infinity < positive_infinity) {
PUTS("TRUE: negative_infinity < positive_infinity");
} else {
num_errors++;
puts("FALSE: negative_infinity < positive_infinity");
}
if (negative_infinity < 0) {
PUTS("TRUE: negative_infinity < 0");
} else {
num_errors++;
puts("FALSE: negative_infinity < 0");
}
if (positive_infinity > 0) {
PUTS("TRUE: positive_infinity > 0");
} else {
num_errors++;
puts("FALSE: positive_infinity > 0");
}
if (num_errors != 0) {
printf("%d test%s failed\n", num_errors, (num_errors == 1) ? "" : "s");
} else {
puts("ALL OK.");
}
exit(EXIT_SUCCESS);
}
如果某些编译器未通过这些测试(已知的异常列在上面的代码中的注释中),这些编译器是什么,是否可以以不同的方式进行?
我已经测试过,没有问题(全部选项:-W -Wall -O3
):
所有GCC都使用选项nan != nan
使第一个测试-ffast-math
失败,但这是记录的行为,如果设置了该选项,GCC定义了一个宏,所以没问题。 MSVC显示与/fp:fast
相同的行为,并且也有记录,但我找不到一种简单的方法来检测它,是否有一个?
答案 0 :(得分:0)
对于Array
,可能会启用“快速数学运算”,编译器只是对操作数进行位测试,而不是进行正确的浮点测试。
NaN永远不应该等于NaN,因为如果操作数不是数字,我们应该如何确定它们的等价数字?因此,绝不应该尝试使用nan != nan
来确定值的NaN-ness。一个人需要一个特殊的功能来做到这一点,可能包括查看位,或让FPU在内部查看位。
如您的代码所示,检测作弊的方法是进行受控测试。