10.8. 比較関数

注記

詳しくは「表:比較関数」(表B.117「表:比較関数」)を参照ください。

比較関数では単純な等号、大小を比べる操作が行なえます。

int any (igentype x)

引数xのどれかの要素の最上位ビットが設定されているときは1、それ以外のときは0を返します。

int all (igentype x)

引数xの全ての要素の最上位ビットが設定されているときは1、それ以外のときは0を返します。

サンプル. 

int8 intvec1 = vload8(0, number);
int8 intvec2 = vload8(0, number);
int8 intvec3 = -1;

int any2 = any(intvec1); //(1)
printf("any2 %d\n", any2);
int any3 = any(intvec3); //(2)
int all3 = all(intvec3); //(3)
printf("any3 and all3 %d : %d\n", any3, all3);

int4 intvec4 = (int4)(1,-1,1,-1);
int4 intvec5 = (int4)(1,0,1,-1);
int4 intvec6 = 0;

int any4 = any(intvec4); //(4)
int all4 = all(intvec4); //(5)
printf("any4 and all4 %d : %d\n", any4, all4);

intvec6 = intvec4 == intvec5;
printf("intvec6 = %v4d\n",intvec6);
int any5 = any(intvec6); //(6)
int all5 = all(intvec6); //(7)
printf("any5 and all5 %d : %d\n", any5, all5);

(1)

符号つきの整数の最上位ビットは負の整数だが、全て正のため0を返します。

(2)

-1の要素が一つでもあるため、1を返します。

(3)

全ての要素が-1のため、1を返します。

(4)

-1の要素が一つでもあるため、1を返します。

(5)

全ての要素が-1でないため、0を返します。

(6)

-1の要素が一つでもあるため、1を返します。

(7)

全ての要素が-1でないため、0を返します。

出力. 

intvec1 = 0,1,2,3,4,5,6,7
intvec2 = 0,1,2,3,4,5,6,7
any2 0
any3 and all3 1 : 1
any4 and all4 1 : 0
intvec6 = -1,0,-1,-1
any5 and all5 1 : 0

allとanyが最も活躍できる分野は自然言語処理です。以下のように、文字列の比較を一括で行なう場合に使用できます。

char4 word1 = (char4) ('a','b','c','\0');
char4 word2 = (char4) ('b','c','d','\0');
char2 word3 = word1.s01 == word2.s12;

int all6 = all(word3);
printf("word match all6: %d\n", all6);

無論、次のような等号の比較をする関数も用意されています。

int isequal (float x, float y)

各要素にたいして、x == yの比較をし、その結果を返します。

int isinf (float)

無限大(正または負)かテストをします。

int isnormal (float)

正規値(0, 非正規化数, 無限大, NaN のいずれでもない)かテストします。

float nan = NAN;
float huge_val = HUGE_VALF;
float infinity = INFINITY;
int is_nan = isnan(nan);
int is_infinity1 = isinf(huge_val); //(1)
int is_infinity2 = isinf(infinity);
int is_normal1 = isnormal(nan); //(2)
int is_normal2 = isnormal(-2.4f);
int is_normal3 = isnormal(0.5f);
int is_normal4 = isnormal(MAXFLOAT+1.0f);
printf("edge test: %d - %d - %d\n", is_nan, is_infinity1, is_infinity2);
printf("normal test1: %d\n", is_normal1);
printf("normal test2: %d\n", is_normal2);
printf("normal test3: %d\n", is_normal3);
printf("normal test4: %d\n", is_normal4);

(1)

無限大(正または負)かテストをします。

(2)

正規値(0, 非正規化数, 無限大, NaN のいずれでもない)かチェック。nanのため0を返します。

出力. 

edge test: 1 - 1 - 1
normal test1: 0
normal test2: 1
normal test3: 1
normal test4: 1

int isordered (float x, float y)

isequal(x, x) && isequal(y, y)の計算をします。

int isunordered (float x, float y)

 xまたはyがNaNのときはゼロ以外、それ以外は0を返します。

int signbit (float)

符号ビットをテストします。符号ビットが設定されているときはゼロ以外、それ以外のときは0を返します。

float4 floatvec1 = (float4)(3.0f, -4.0f, 5.0f, -6.0f);
int4 sign = signbit(floatvec1); //(1)
printf("signbit test: %#v4d\n", sign);

int ordered1 = isordered(floatvec1.x, floatvec1.z); //(2)
printf("isordered test: %d\n", ordered1);

int ordered2 = isunordered(floatvec1.x, floatvec1.z); //(3)
printf("isunordered test: %d\n", ordered2);

(1)

符号を抽出します。

(2)

isequal(x, x) && isequal(y, y)の計算結果を返します。

(3)

NaNではないため、0を返します。

signbit test: 0,-1,0,-1
isordered test: 1
isunordered test: 0

10.8.1. 比較分岐

注記

詳しくは「表:比較関数」(表B.117「表:比較関数」)を参照ください。

GPU等の専用デバイスでは、if構文などの分岐は一番苦手とするところで、(筆者は例外を知らないが)大半のベンダーがif文を使わないように推奨しています。

OpenCLの組込みbitselect、select関数では分岐を避けることができます。まずbitselect関数を見てみましょう。

gentype bitselect (
    gentype a,
    gentype b,
    gentype c)

この関数は戻り値のビットを条件により選別、設定します。変数cの対応するビットが0のときは、aのビットそれ以外のときは、bのビットを返します。

uchar4 bit_result;
uchar4 bit_mask = (uchar4)(0x00,0x99,0x00,0x99);
uchar4 bit_input1 = (uchar4)(0x55,0x55,0x55,0x55);
uchar4 bit_input2 = (uchar4)(0xFF,0xFF,0xFF,0xFF);
bit_result = bitselect(bit_input1,bit_input2,bit_mask);
printf("Result vector is %#v4u\n", bit_result);
Result vector is 85,221,85,221

この場合、0x55が85なので、221を見ればビットセレクトが処理した内容が理解できます。

mask
1
0
0
1
1
0
0
1
a
1
1
1
1
1
1
1
1
b
0
1
0
1
0
1
0
1
戻り値(十進数)
128
64
0
16
8
4
0
1

戻ったビットの和は221となります。

次により単純な分岐処理ができるselect関数を見てみます。

gentype select (
    gentype a,
    gentype b,
    igentype c)
gentype select (
    gentype a, gentype b,
    ugentype c)

ベクトル型のとき c[i] の最上位ビット(MSB)から ? b[i] : a[i] を計算します。

スカラ型のときは、c ? b : aを計算します。

それでは実装例を見てみましょう。

float4 in1 = (float4)(1.5f, 15.0f,5.0f,7.5f);
float4 in2 = (float4)(-20.0f, -5.0f, 1.0f, 100.0f);
int4 mask = (int4)(-1,0,0,-1);

printf("in1 vector is %#v4f\n", in1);
printf("in2 vector is %#v4f\n", in2);
printf("mask vector is %#v4d\n", mask);

float4 result = select(in1, in2, mask);
printf("Result vector is %#v4f\n", result);

出力. 

in1 vector is 1.500000,15.000000,5.000000,7.500000
in2 vector is -20.000000,-5.000000,1.000000,100.000000
mask vector is -1,0,0,-1
Result vector is -20.000000,15.000000,5.000000,100.000000

int mask_sc = 1;
float in1_sc = 20.0f;
float in2_sc = 10.0f;
printf("in1 scalar is %#f\n", in1_sc);
printf("in2 scalar is %#f\n", in2_sc);
printf("mask scalar is %#d\n", mask_sc);
float scalar_result = select(in1_sc,in2_sc,mask_sc);
printf("Result scalar is %f\n", scalar_result);

出力. 

in1 scalar is 20.000000
in2 scalar is 10.000000
mask scalar is 1
Result scalar is 10.000000

最上ビットを使うベクトル型に対して、スカラ型はif分岐のように使えます。これは分岐の苦手なGPUでは分岐の代用となります。

Copyright 2018-2019, by Masaki Komatsu