今回は、IEEE基準にて、C言語で網羅的に浮動小数点乱数を生成する方法について議論する。前回までに議論したテストフレームワークを制作するのに必要である。
なぜ、IEEE基準にて、網羅的な乱数が必要であり、それを作る必要があるかというと、IntelのIntrinsic関数とそれをRISC-Vにポートしたもののずれなどのエラーは、どこに潜んでいるかわからず、網羅的に浮動小数点乱数を生成する必要があります。そして、網羅的に浮動小数点を生成するやり方は、使用用途がかなり限られます。
ただし、C言語で浮動小数点乱数を発生させる組み込み関数がないのと、他の言語の浮動小数点乱数がIEEE基準にて、網羅的でない。よって、今回作ってみたいと思う。
この記事を作るにあたって、一部のソースコードを生成するために、ChatGTPを使用しました。本文にはChatGTPは使用していません。
//このソースコードはChatGTPの出力です
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
// Function to generate a random IEEE standard floating-point number
float generateRandomFloat() {
// Generate a random integer
uint32_t randomInt = rand();
// Convert the integer to a floating-point number
float randomFloat = *((float*)&randomInt);
return randomFloat;
}
int main() {
// Seed the random number generator with the current time
srand((unsigned int)time(NULL));
// Number of random values to generate
const int numValues = 10;
// Generate and print random IEEE standard floating-point numbers
printf("Random IEEE Standard Floating-Point Numbers:\n");
for (int i = 0; i < numValues; ++i) {
float randomFloat = generateRandomFloat();
printf("%f\n", randomFloat);
}
return 0;
}
C言語のrand()関数は、uint32_tの最大値から最小値まで網羅的に値が出てくれないと、randomFloatが網羅的になってくれないわけですが、rand()関数の最大値と最小値が、数値型の最大値と最小値と同じでない場合がある。よって、このままのビルトインのrand()関数のままでは使えない。
よって、符号なし整数の値を、網羅的に生成するランダム関数を制作しなければならない。よって、上のソースコードのrand()関数を置換しなければならない。
さらに、以下のようなポートのソースコードを見たら分かるように、ポートにおいては、__m128d形式となっている。よって、IEEE形式の網羅的なランダムな数字を__128d形式などに変換する必要がある。
//Linux on Power porting guideより抜粋
extern __inline __m128d __attribute__((__gnu_inline__, __always_inline__,__artificial__))
_mm_add_pd (__m128d __A, __m128d __B)
{
return (__m128d) ((__v2df)__A + (__v2df)__B);
}
__m128dはベクトル形式であり、内包するスカラーは、倍精度(64ビット)浮動小数点形式です。よって、ChatGTPが生成したソースコードはそのままでは使えません。
まずは、uint64_t形式で、符号なし整数で網羅的に乱数を生成するC言語の関数を制作する必要があります。
//ChatGTPの生成したコードを一部抜粋
uint64_t generateRandomUInt64() {
// rand()関数を使用して32ビットの乱数を生成し、それを2回組み合わせて64ビットにする
uint64_t randomValue = ((uint64_t)rand() << 32) | rand();
return randomValue;
}
ただし、rand()がそもそも確実に網羅的でないとなりません。よって、rand()を置換します。ここでは、intを生成していますが、これで、ビットレベルでランダムな値が生成されるはずです。randComplehensive()とします。
//ChatGTPで生成されたコードをもとに改変しました
// 指定された最小値と最大値の範囲で符号なし整数をランダムに生成する関数
#define UINT32_MIN 0
uint32_t randComplehensive() {
// 範囲内の乱数を生成し、最小値を加えて指定された範囲に調整する
uint32_t randomValue = arc4random() % (UINT32_MAX - UINT32_MIN + 1) + UINT32_MIN;
return randomValue;
}
全体的には以下のようになります。
//ChatGTPの出力を一部使いつつ、自分で組みました。
#define UINT32_MIN 0
uint32_t randComplehensive32() {
// 範囲内の乱数を生成し、最小値を加えて指定された範囲に調整する
uint32_t randomValue = arc4random() % (UINT32_MAX - UINT32_MIN + 1) + UINT32_MIN;
return randomValue;
}
uint64_t randComplehensive64() {
// rand()関数を使用して32ビットの乱数を生成し、それを2回組み合わせて64ビットにする
uint64_t randomValue = ((uint64_t)randComplehensive32() << 32) | randComplehensive32();
return randomValue;
}
全体的な走らせられるソースコードは以下のようになる。
//ChatGTPの出力を一部使いつつ、自分で組みました。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#define UINT32_MIN 0
uint32_t randComplehensive32() {
// 範囲内の乱数を生成し、最小値を加えて指定された範囲に調整する
uint32_t randomValue = arc4random() % (UINT32_MAX - UINT32_MIN) + UINT32_MIN;
return randomValue;
}
uint64_t randComplehensive64() {
// rand()関数を使用して32ビットの乱数を生成し、それを2回組み合わせて64ビットにする
uint64_t randomValue = ((uint64_t)randComplehensive32() << 32) | randComplehensive32();
return randomValue;
}
// Function to generate a random IEEE standard floating-point number
float generateRandomFloat() {
// Generate a random integer
uint64_t randomInt = randComplehensive64();
// Convert the integer to a floating-point number
double randomFloat = *((float*)&randomInt);
return randomFloat;
}
int main() {
// Seed the random number generator with the current time
srand((unsigned int)time(NULL));
// Number of random values to generate
const int numValues = 10;
// Generate and print random IEEE standard floating-point numbers
printf("Random IEEE Standard Floating-Point Numbers:\n");
for (int i = 0; i < numValues; ++i) {
double randomFloat = generateRandomFloat();
printf("%f\n", randomFloat);
}
return 0;
}
結果は以下のようになる。
$./a.out
Random IEEE Standard Floating-Point Numbers:
0.000000
486831555078751518720.000000
98254927517225844736.000000
1351486355761673295907337297687740416.000000
0.000000
25428.884766
0.000000
0.000000
8553342481080469833121792.000000
24939111383040.000000
おそらく作ろうとしているテストフレームワーク以外では使えない乱数が出来た。0.000000が多いのが気になるので、ビットパターンも出力してみる。
//ChatGTPが出力したコードを抜粋
void printDoubleBitPattern(double value) {
// double型変数のアドレスをunsigned char型のポインタにキャスト
unsigned char *bytePointer = (unsigned char *)&value;
// バイト列をビットパターンとして表示
printf("Double: %lf\n", value);
printf("Bit Pattern: ");
// バイト列の各バイトを表示
for (int i = sizeof(double) - 1; i >= 0; --i) {
for (int j = 7; j >= 0; --j) {
// ビットごとに表示
printf("%d", (bytePointer[i] >> j) & 1);
}
printf(" ");
}
printf("\n");
}
出力は以下のようになる
Random IEEE Standard Floating-Point Numbers:
Double: 0.000000
Bit Pattern: 00111011 11000110 01001010 01000001 00000000 00000000 00000000 00000000
Double: 19834312295004656599495081984.000000
Bit Pattern: 01000101 11010000 00000101 10100011 11000000 00000000 00000000 00000000
Double: 24021154901434798809251552884791705600.000000
Bit Pattern: 01000111 10110010 00010010 01001110 01100000 00000000 00000000 00000000
Double: 0.000000
Bit Pattern: 00111101 01111001 11011011 10011010 10100000 00000000 00000000 00000000
Double: 236571278179604675690496.000000
Bit Pattern: 01000100 11001001 00001100 01000111 00000000 00000000 00000000 00000000
Double: 6156333725746375537532600320.000000
Bit Pattern: 01000101 10110011 11100100 01100110 01100000 00000000 00000000 00000000
Double: 347202966488275417575349813248.000000
Bit Pattern: 01000110 00010001 10000111 01111110 01000000 00000000 00000000 00000000
Double: 149384047796158464.000000
Bit Pattern: 01000011 10000000 10010101 11000000 00000000 00000000 00000000 00000000
Double: 0.000000
Bit Pattern: 00111000 11011001 00101000 10011110 00100000 00000000 00000000 00000000
Double: 0.000000
Bit Pattern: 00111000 00100101 10111110 00011101 11100000 00000000 00000000 00000000
値が0.000000に偏っている気がする・・・・・・・まあ、複数のビットパターンが0.000000になるのは考えられることだ。今回のテストフレームワークにおいてランダム値は、まんべんなく値を出したいわけではない。
ではここで、__m128d形式に変換するにはどうすればいいか。続きは次回以降書く。
追記:色々間違いが見つかってますので修正します