我关注以下情况
min(-0.0,0.0)
max(-0.0,0.0)
minmag(-x,x)
maxmag(-x,x)
根据维基百科 IEEE 754-2008所述,关于最小值和最大值的定义有些余地,当输入值相等但表示不同的情况下会留下一些余地。特别地:
min(+0,−0)或min(−0,+0)必须产生一个值为零的结果,但可以始终返回第一个参数。
最小值和最大值操作已被定义,但在输入值相等但表示不同的情况下会留下一些余地。特别地:
我进行了一些测试,比较了如下所定义的fmin
、fmax
、min和max。
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
还有_mm_min_ps
和_mm_max_ps
,它们调用SSE的minps
和maxps
指令。
以下是结果(我用来测试的代码已经发布在下面)
fmin(-0.0,0.0) = -0.0
fmax(-0.0,0.0) = 0.0
min(-0.0,0.0) = 0.0
max(-0.0,0.0) = 0.0
_mm_min_ps(-0.0,0.0) = 0.0
_mm_max_ps(-0.0,0.0) = -0.0
正如您所看到的,每个案例都返回不同的结果。所以我的主要问题是C和C++标准库怎么说?fmin(-0.0,0.0)是否必须等于-0.0,fmax(-0.0,0.0)是否必须等于0.0,还是允许不同的实现对其进行不同的定义?如果这是实现定义的,这是否意味着为确保代码与C标准库(例如来自不同编译器的)的不同实现兼容,必须进行检查,以确定它们如何实现最小值和最大值?
那minmag(-x,x)和maxmag(-x,x)呢?这两者在IEEE 754-2008中都有定义。至少在IEEE 754-2008中,它们是实现定义吗?我从Wikipedia对min和max的评论推断出这些是实现定义的。但据我所知,C标准库没有定义这些函数。在OpenCL中,这些函数被定义为:OpenCL
如果 |x| > |y|,则返回 x;如果 |y| > |x|,则返回 y;否则返回 fmax(x, y)。
如果 |x| < |y|,则返回 x;如果 |y| < |x|,则返回 y;否则返回 fmin(x, y)。
x86指令集中没有minmag和maxmag指令,因此我必须实现它们。但在我的情况下,我需要性能,并且为相等的情况创建分支不是高效的。
Itaninum指令集有minmag和maxmag指令(famin和famax),据我所知(从阅读中),在这种情况下它返回第二个参数。然而,minps和maxps似乎并不是这样做的。奇怪的是,_mm_min_ps(-0.0,0.0)= 0.0,_mm_max_ps(-0.0,0.0)= -0.0。我原以为它们会在两种情况下返回第一个参数或第二个参数。为什么minps和maxps指令被定义成这样呢?#include <stdio.h>
#include <x86intrin.h>
#include <math.h>
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
int main(void) {
float a[4] = {-0.0, -1.0, -2.0, -3.0};
float b[4] = {0.0, 1.0, 2.0, 3.0};
__m128 a4 = _mm_load_ps(a);
__m128 b4 = _mm_load_ps(b);
__m128 c4 = _mm_min_ps(a4,b4);
__m128 d4 = _mm_max_ps(a4,b4);
{ float c[4]; _mm_store_ps(c,c4); printf("%f %f %f %f\n", c[0], c[1], c[2], c[3]); }
{ float c[4]; _mm_store_ps(c,d4); printf("%f %f %f %f\n", c[0], c[1], c[2], c[3]); }
printf("%f %f %f %f\n", fmin(a[0],b[0]), fmin(a[1],b[1]), fmin(a[2],b[2]), fmin(a[3],b[3]));
printf("%f %f %f %f\n", fmax(a[0],b[0]), fmax(a[1],b[1]), fmax(a[2],b[2]), fmax(a[3],b[3]));
printf("%f %f %f %f\n", min(a[0],b[0]), min(a[1],b[1]), min(a[2],b[2]), min(a[3],b[3]));
printf("%f %f %f %f\n", max(a[0],b[0]), max(a[1],b[1]), max(a[2],b[2]), max(a[3],b[3]));
}
//_mm_min_ps: 0.000000, -1.000000, -2.000000, -3.000000
//_mm_max_ps: -0.000000, 1.000000, 2.000000, 3.000000
//fmin: -0.000000, -1.000000, -2.000000, -3.000000
//fmax: 0.000000, 1.000000, 2.000000, 3.000000
//min: 0.000000, -1.000000, -2.000000, -3.000000
//max: 0.000000, 1.000000, 2.000000, 3.000000
编辑:
关于C++,我测试了std::min(-0.0,0.0)
和std::max(-0.0,0.0)
,它们都返回-0.0
。这表明std::min
与fmin
不同,std::max
与fmax
也不同。
min(-0.0,0.0)
和max(-0.0,0.0)
的情况下并不重要,但在minmag(-x,x)
和maxmag(-x,x)
中确实很重要。 - Z boson