目次
1.はじめに
多くのプログラマが、float
型やdouble
型で浮動小数点を使用した演算プログラムを記述します。知っての通り float
型は単精度浮動小数点として、また double
型は倍精度浮動小数点として、多くの場合、IEEE754 規格で定義されています。
IEEE754 規格では、5 種類の「浮動小数点例外」を定義しています。
本稿では、この浮動小数点例外を検出する為のソースコードを用いて、複数の処理系を使用して、稚拙ながら FPGA に実装する手段を、あれこれ模索してみました。
本稿の執筆にあたり、筆者の技量では不十分な側面もあるので、以下の記事を参考にしました。
-
FLP03-C. 浮動小数点エラーを検知して処理する
https://www.jpcert.or.jp/sc-rules/c-flp03-c.html -
浮動小数点数型(float型 double型)の最大値と最小値 - float.h
https://webkaru.net/clang/float-header/ -
例外と例外処理
https://docs.oracle.com/cd/E19957-01/806-4847/ncg_handle.html
2. 環境
-
WSL(Windows Subsystem for Linux)
- GNU C++11 (Ubuntu 7.5.0-3ubuntu1~18.04) version 7.5.0 (x86_64-linux-gnu)
-
Microsoft Visual Studio Express 2017
-
インテル® Quartus® Prime Pro Edition version 22.2
- RiscFree™IDE For Intel® FPGAs
- The CXX compiler identification is GNU 11.2.0
- Platform Designer Pro Edition
- Nios® V Processor
- DSP Builder Advanced Blockset
- RiscFree™IDE For Intel® FPGAs
-
MATLAB® R2021a
- MATLAB®
- Simulink®
-
インテル® Cyclone® 10 GX FPGA 開発キット
3. 浮動小数点例外の種類
IEEE754 浮動小数点例外では、次の 5 つの例外が定義されています。
-
無効例外 ( Invalid )
- 例えは、負数の平方根を求めようとした場合に発生
- デフォルトでは非数(qNaN)を返す
-
ゼロ除算例外 ( Zero Division by zero )
- 1÷0やlog(O)なとで発生
- デフォルトでは±∞を返す
-
オ-バ-フロ-例外 ( Overflow )
- 正しく表現できないほど大きくなった場合に発生
- 最近接丸めモ-ドの場合、デフォルトでは±∞を返す
-
アンダ-フロ-例外 ( Underflow )
- 正規数で表現できないほどに小さく、非0であるが不正確な結果となった場合に発生
- デフォルトでは非正規化数を返す
-
不正確例外 ( Inexact )
- 丸められた有効な演算結果が、真の演算結果と異なる場合に発生
- 演算結果の精度落ちて低下した場合に発生
- ほとんどの浮動小数点演算では、この例外が発生する模様
- デフォルトでは指定されたモ-ドの丸めを施した結果を返す
4. 浮動小数点例外検出ソースコードと実行結果
浮動小数点例外の検出を目的に、次のソースコードを使用しました。
各演算に対して、どの例外が検出されるのかの予測を行い、それをコメント行に記載しています。
#include <iostream>
#include <cfenv>
#include <math.h>
#include <fenv.h>
#include <float.h>
#include <iomanip>
using namespace std;
/* ****** 演算例: 少ないほどヒープのサイズを確保できる ****** */
#define UT_EX_1 // 負数の平方根
#define UT_EX_2 // 単精度データに、倍精度データを入力
#define UT_EX_3 // 倍精度データに、倍精度データを入力
#define UT_EX_4 // ゼロで除算
#define UT_EX_5 // 割り切れない割り算
#define UT_EX_6 // 倍精度浮動小数点の下限値 DBL_MIN を表示
#define UT_EX_7 // 倍精度浮動小数点の下限値 DBL_MIN の 0.1 倍
#define UT_EX_8 // 倍精度浮動小数点の上限値 DBL_MAX を表示
#define UT_EX_9 // 倍精度浮動小数点の上限値 DBL_MAX の 10 倍
class u_test_num {
public:
int num;
};
class u_test_1 {
public:
int n = -1;
double r = 0;
};
class u_test_2to3 {
public:
double a = 1e-40;
float ys;
double yd = 0.0;
};
class u_test_4 {
public:
double x = 0.0;
double y = 1.0;
double b;
};
class u_test_5 {
public:
double quot; // 商
double numer = 0.0; // 分子
double demonmin = 0.0; // 分母
};
class u_test_6to9 {
public:
double dd;
};
int exception_judge(void);
void div_spacer(void);
void div_spacer_2(void);
void test_num(int);
#ifdef UT_EX_1
void test_1(void);
#endif
#ifdef UT_EX_2
void test_2(void);
#endif
#ifdef UT_EX_3
void test_3(void);
#endif
#ifdef UT_EX_4
void test_4(void);
#endif
#ifdef UT_EX_5
void test_5(void);
#endif
#ifdef UT_EX_6
void test_6(void);
#endif
#ifdef UT_EX_7
void test_7(void);
#endif
#ifdef UT_EX_8
void test_8(void);
#endif
#ifdef UT_EX_9
void test_9(void);
#endif
/* **********************************************************************************
* メイン
* ********************************************************************************* */
int main(void)
{
#ifdef UT_EX_1
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(1);
/* ========== 負数の平方根: 無効例外(Invalid) を検出 ========== */
/* --------------->> 検出される例外: 無効 ---------- */
test_1();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_2
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(2);
/* ========== 単精度データに、倍精度データを入力 ========== */
/* --------------->> 検出される例外: 不正確 ---------- */
/* --------------->> 検出される例外: アンダーフロー -- */
test_2();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_3
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(3);
/* ========== 倍精度データに、倍精度データを入力 ========== */
/* --------------->> 検出される例外: なし ---------- */
test_3();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_4
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(4);
/* ==================== ゼロで除算 ========================= */
/* --------------->> 検出される例外: ゼロ除算 -------- */
test_4();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_5
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(5);
/* ==================== 割り切れない割り算 ========================= */
/* ------------------>> 検出される例外: 不正確 --------------- */
test_5();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_6
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(6);
/* =============== 倍精度浮動小数点の下限値 DBL_MIN を表示 ========= */
/* --------------->> 検出される例外: なし ------------------ */
test_6();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_7
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(7);
/* =============== 倍精度浮動小数点の下限値 DBL_MIN の 0.1 倍 ============ */
/* --------------->> 検出される例外: アンダーフロー ---------------- */
test_7();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_8
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(8);
/* =============== 倍精度浮動小数点の上限値 DBL_MAX を表示 ========= */
/* --------------->> 検出される例外: なし ------------------ */
test_8();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_9
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(9);
/* =============== 倍精度浮動小数点の上限値 DBL_MAX の 10 倍 ============= */
/* --------------->> 検出される例外: オーバーフロー ---------------- */
test_9();
/* 浮動小数点例外判定 */
exception_judge();
#endif
/* ******************** 終了 *************************** */
div_spacer();
return 0;
}
/* ******************************************************************* */
/* 浮動小数点例外判定 */
/* ******************************************************************* */
int exception_judge(void)
{
/* ゼロ除算例外 */
if (std::fetestexcept(FE_DIVBYZERO)) {
std::cout << " DIVZERO Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No DIVZERO Exception )\n" << std::endl;
}
/* 無効例外 */
if (std::fetestexcept(FE_INEXACT)) {
std::cout << " Inexact Result Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No Inexact Result Exception )\n" << std::endl;
}
/* 不正確例外 */
if (std::fetestexcept(FE_INVALID)) {
std::cout << " Invalid Argument Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No Invalid Argument Exception )\n" << std::endl;
}
/* オーバーフロー例外 */
if (std::fetestexcept(FE_OVERFLOW)) {
std::cout << " Overflow Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No Overflow Exception )\n" << std::endl;
}
/* アンダーフロー例外 */
if (std::fetestexcept(FE_UNDERFLOW)) {
std::cout << " Underflow Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No Underflow Exception )\n" << std::endl;
}
/* 全ての浮動小数点例外をクリア */
std::feclearexcept(FE_ALL_EXCEPT);
return 0;
}
/* ******************************************************************* */
/* 区切り */
/* ******************************************************************* */
void div_spacer(void)
{
std::cout << "\n=========================================================\n" << std::endl;
}
//void div_spacer_2(void)
//{
// std::cout << "\n ------------------------------------------------------- \n" << std::endl;
//}
void test_num(int num)
{
u_test_num num_dply;
num_dply.num = num;
std::cout << "No. " << num_dply.num << "\n" << std::endl;
}
/* ******************************************************************* */
/* 演算例 */
/* ******************************************************************* */
#ifdef UT_EX_1
void test_1(void)
{
u_test_1 ex1;
ex1.r = sqrt(ex1.n); /*平方根*/
/* ========== 負数の平方根: 無効例外(Invalid) を検出 ========== */
/* --------------->> 検出される例外: 無効 ---------- */
std::cout << " Sqrt(" << ex1.n << ") = " << scientific << setprecision(16) << ex1.r << "\n" << std::endl;
}
#endif
#ifdef UT_EX_2
void test_2(void)
{
u_test_2to3 ex2;
ex2.ys = ex2.a;
/* ========== 単精度データに、倍精度データを入力 ========== */
/* --------------->> 検出される例外: 不正確 ---------- */
/* --------------->> 検出される例外: アンダーフロー -- */
std::cout << " Result: ys = " << scientific << setprecision(16) << ex2.ys << "\n" << std::endl;
}
#endif
#ifdef UT_EX_3
void test_3(void)
{
u_test_2to3 ex3;
ex3.yd = ex3.a;
/* ========== 倍精度データに、倍精度データを入力 ========== */
/* --------------->> 検出される例外: なし ---------- */
std::cout << " Result: yd = " << scientific << setprecision(16) << ex3.yd << "\n" << std::endl;
}
#endif
#ifdef UT_EX_4
void test_4(void)
{
u_test_4 ex4;
ex4.b = ex4.y / ex4.x;
/* ==================== ゼロで除算 ========================= */
/* --------------->> 検出される例外: ゼロ除算 -------- */
//std::cout << " Result of (DBL_MIN): dd = " << ex6.dd << "\n" << std::endl;
std::cout << " Result: b = " << scientific << setprecision(16) << ex4.b << "\n" << std::endl;
}
#endif
#ifdef UT_EX_5
void test_5(void)
{
u_test_5 ex5;
ex5.quot; // 商
ex5.numer = 2.0; // 分子
ex5.demonmin = 3.0; // 分母
ex5.quot = ex5.numer / ex5.demonmin;
/* ==================== 割り切れない割り算 ========================= */
/* ------------------>> 検出される例外: 不正確 --------------- */
std::cout << " Result: 2.0/3.0 = " << scientific << setprecision(16) << ex5.quot << "\n" << std::endl;
}
#endif
#ifdef UT_EX_6
void test_6(void)
{
u_test_6to9 ex6;
ex6.dd = DBL_MIN;
/* =============== 倍精度浮動小数点の下限値 DBL_MIN を表示 ========= */
/* --------------->> 検出される例外: なし ------------------ */
std::cout << " Result of (DBL_MIN): dd = " << scientific << setprecision(16) << ex6.dd << "\n" << std::endl;
}
#endif
#if defined UT_EX_7
void test_7(void)
{
u_test_6to9 ex7;
ex7.dd = DBL_MIN * (double)(0.1);
/* =============== 倍精度浮動小数点の下限値 DBL_MIN の 0.1 倍 ============ */
/* --------------->> 検出される例外: アンダーフロー ---------------- */
std::cout << " Result of (DBL_MIN * 0.1): dd = " << scientific << setprecision(16) << ex7.dd << "\n" << std::endl;
}
#endif
#if defined UT_EX_8
void test_8(void)
{
u_test_6to9 ex8;
ex8.dd = DBL_MAX;
/* =============== 倍精度浮動小数点の下限値 DBL_MAX を表示 ========= */
/* --------------->> 検出される例外: なし ------------------ */
std::cout << " Result of (DBL_MAN): dd = " << scientific << setprecision(16) << ex8.dd << "\n" << std::endl;
}
#endif
#if defined UT_EX_9
void test_9(void)
{
u_test_6to9 ex9;
ex9.dd = DBL_MAX * (double)(10.0);
/* =============== 倍精度浮動小数点の上限値 DBL_MAX の 10 倍 ============= */
/* --------------->> 検出される例外: オーバーフロー ---------------- */
std::cout << " Result of (DBL_MIN * 10.0): dd = " << scientific << setprecision(16) << ex9.dd << "\n" << std::endl;
}
#endif
4.1 WSL での実行結果
最初に、WSL から以下のコマンドを入力して、結構結果を得ました。
WSL はPCにインストールしているので、実行形式ファイルは PC に内蔵されているインテル® Core™ i7-10510U CPU で処理されます。
- コンパイル・オプション
g++ -o fpexception_ck_g.elf -std=c++11 -v fpexception_ck_g.cpp
- 実行コマンド
./fpexception_ck_g.elf
- 実行結果
=========================================================
No. 1
Sqrt(-1) = -nan
( No DIVZERO Exception )
( No Inexact Result Exception )
Invalid Argument Exception <<======== Detect !!
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 2
Result: ys = 9.9999461011147596e-41
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
( No Overflow Exception )
Underflow Exception <<======== Detect !!
=========================================================
No. 3
Result: yd = 9.9999999999999993e-41
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 4
Result: b = inf
DIVZERO Exception <<======== Detect !!
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 5
Result: 2.0/3.0 = 6.6666666666666663e-01
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 6
Result of (DBL_MIN): dd = 2.2250738585072014e-308
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 7
Result of (DBL_MIN * 0.1): dd = 2.2250738585072034e-309
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 8
Result of (DBL_MAN): dd = 1.7976931348623157e+308
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 9
Result of (DBL_MIN * 10.0): dd = inf
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
Overflow Exception <<======== Detect !!
( No Underflow Exception )
=========================================================
Tips
下記の記事を参考にして、WSL に g++ と make をインストールています。
-
【Windows10/WSL Ubuntu】gcc, make などの開発ツールをインストールして使う
https://www.yokoweb.net/2018/03/02/windows10-wsl-gcc-install/ -
Windows Subsystem for Linux(WSL)でGCCをインストールしてみた!
https://www.lisz-works.com/entry/wsl-install-gcc -
WSL を使って make, g++ を実行してみる
https://qiita.com/KentoNaka/items/531f8af8872bfec2d82a
実行結果から、CPU が浮動小数点例外を検出していることが把握できます。
4.2 Visual Studio での実行結果
次に、このソースコードを、Visual Studio でも実行しました。
実行結果は、以下の通りです。
- 実行結果
=========================================================
No. 1
Sqrt(-1) = -nan(ind)
( No DIVZERO Exception )
( No Inexact Result Exception )
Invalid Argument Exception <<======== Detect !!
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 2
Result: ys = 9.9999461011147596e-41
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
( No Overflow Exception )
Underflow Exception <<======== Detect !!
=========================================================
No. 3
Result: yd = 9.9999999999999993e-41
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 4
Result: b = inf
DIVZERO Exception <<======== Detect !!
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 5
Result: 2.0/3.0 = 6.6666666666666663e-01
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 6
Result of (DBL_MIN): dd = 2.2250738585072014e-308
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 7
Result of (DBL_MIN * 0.1): dd = 2.2250738585072034e-309
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 8
Result of (DBL_MAN): dd = 1.7976931348623157e+308
( No DIVZERO Exception )
Inexact Result Exception <<======== Detect !!
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
No. 9
Result of (DBL_MIN * 10.0): dd = inf
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
同じ パソコンの CPU で実行したにもかかわらず検出結果の一部に差異が生じているのは、異なる処理系依存、とりわけ GNU と Visual Studio のコンパイラが行う丸め処理に起因していると推察しています。
4.3 ソフトコア・プロセッサ Nios® V での実行結果
インテル® FPGA に実装可能な RISC-V ソフトコア・プロセッサとして、Nios® V でも実行を行いました。
Nios® V 向けに GNU コンパイラが提供されているので、同じくGNU をベースとした WSL での実行結果と一致することが期待できます。
Platform Designer を使用して、Cyclone® 10 GX FPGA 開発キット向けにデザインを構築しましたが、On-Chip Memory ブートを採用してしまったので実行時のメモリ・サイズが不足気味となり、苦肉の策として、メモリ・サイズが少なくなるように、ライブラリの追加を極力減らし、実行する演算(関数で管理)を一つだけ実装したコードに変更しました。
//#include <string>
#include <iostream>
//#include <cfenv>
#include <math.h>
#include <fenv.h>
//#include <float.h>
//#include <iomanip>
//using namespace std;
#define DBL_MAX 1.79769313486231571e+308
#define DBL_MIN 2.22507385850720138e-308
/* ****** 演算例: 少ないほどヒープのサイズを確保できる ****** */
//#define UT_EX_1 // 負数の平方根
//#define UT_EX_2 // 単精度データに、倍精度データを入力
//#define UT_EX_3 // 倍精度データに、倍精度データを入力
#define UT_EX_4 // ゼロで除算
//#define UT_EX_5 // 割り切れない割り算
//#define UT_EX_6 // 倍精度浮動小数点の下限値 DBL_MIN を表示
//#define UT_EX_7 // 倍精度浮動小数点の下限値 DBL_MIN の 0.1 倍
//#define UT_EX_8 // 倍精度浮動小数点の上限値 DBL_MAX を表示
//#define UT_EX_9 // 倍精度浮動小数点の上限値 DBL_MAX の 10 倍
class u_test_num{
public:
int num;
};
class u_test_1{
public:
int n = -1;
double r = 0;
};
class u_test_2to3{
public:
double a = 1e-40;
float ys;
double yd = 0.0;
};
class u_test_4{
public:
double x = 0.0;
double y = 1.0;
double b;
};
class u_test_5{
public:
double quot; // 商
double numer = 0.0; // 分子
double demonmin = 0.0; // 分母
};
class u_test_6to9{
public:
double dd;
};
int exception_judge(void);
void div_spacer(void);
void div_spacer_2(void);
void test_num(int);
#ifdef UT_EX_1
void test_1(void);
#endif
#ifdef UT_EX_2
void test_2(void);
#endif
#ifdef UT_EX_3
void test_3(void);
#endif
#ifdef UT_EX_4
void test_4(void);
#endif
#ifdef UT_EX_5
void test_5(void);
#endif
#ifdef UT_EX_6
void test_6(void);
#endif
#ifdef UT_EX_7
void test_7(void);
#endif
#ifdef UT_EX_8
void test_8(void);
#endif
#ifdef UT_EX_9
void test_9(void);
#endif
/* **********************************************************************************
* main function
* ********************************************************************************* */
int main(void)
{
#ifdef UT_EX_1
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(1);
/* ========== 負数の平方根: 無効例外(Invalid) を検出 ========== */
/* --------------->> 検出される例外: 無効 ---------- */
test_1();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_2
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(2);
/* ========== 単精度データに、倍精度データを入力 ========== */
/* --------------->> 検出される例外: 不正確 ---------- */
/* --------------->> 検出される例外: アンダーフロー -- */
test_2();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_3
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(3);
/* ========== 倍精度データに、倍精度データを入力 ========== */
/* --------------->> 検出される例外: なし ---------- */
test_3();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_4
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(4);
/* ==================== ゼロで除算 ========================= */
/* --------------->> 検出される例外: ゼロ除算 -------- */
test_4();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_5
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(5);
/* ==================== 割り切れない割り算 ========================= */
/* ------------------>> 検出される例外: 不正確 --------------- */
test_5();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_6
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(6);
/* =============== 倍精度浮動小数点の下限値 DBL_MIN を表示 ========= */
/* --------------->> 検出される例外: なし ------------------ */
test_6();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_7
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(7);
/* =============== 倍精度浮動小数点の下限値 DBL_MIN の 0.1 倍 ============ */
/* --------------->> 検出される例外: アンダーフロー ---------------- */
test_7();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_8
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(8);
/* =============== 倍精度浮動小数点の上限値 DBL_MAX を表示 ========= */
/* --------------->> 検出される例外: なし ------------------ */
test_8();
/* 浮動小数点例外判定 */
exception_judge();
#endif
#ifdef UT_EX_9
/* ******************** 浮動小数点計算式 *************************** */
div_spacer();
test_num(9);
/* =============== 倍精度浮動小数点の上限値 DBL_MAX の 10 倍 ============= */
/* --------------->> 検出される例外: オーバーフロー ---------------- */
test_9();
/* 浮動小数点例外判定 */
exception_judge();
#endif
/* ******************** 終了 *************************** */
div_spacer();
return 0;
}
/* ******************************************************************* */
/* 浮動小数点例外判定 */
/* ******************************************************************* */
int exception_judge(void)
{
/* ゼロ除算例外 */
if (std::fetestexcept(FE_DIVBYZERO)) {
std::cout << " DIVZERO Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No DIVZERO Exception )\n" << std::endl;
}
/* 無効例外 */
if (std::fetestexcept(FE_INEXACT)) {
std::cout << " Inexact Result Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No Inexact Result Exception )\n" << std::endl;
}
/* 不正確例外 */
if (std::fetestexcept(FE_INVALID)) {
std::cout << " Invalid Argument Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No Invalid Argument Exception )\n" << std::endl;
}
/* オーバーフロー例外 */
if (std::fetestexcept(FE_OVERFLOW)) {
std::cout << " Overflow Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No Overflow Exception )\n" << std::endl;
}
/* アンダーフロー例外 */
if (std::fetestexcept(FE_UNDERFLOW)) {
std::cout << " Underflow Exception <<======== Detect !!\n" << std::endl;
}
else {
std::cout << " ( No Underflow Exception )\n" << std::endl;
}
/* 全ての浮動小数点例外をクリア */
std::feclearexcept(FE_ALL_EXCEPT);
return 0;
}
/* ******************************************************************* */
/* 区切り */
/* ******************************************************************* */
void div_spacer(void)
{
std::cout << "\n=========================================================\n" << std::endl;
}
//void div_spacer_2(void)
//{
// std::cout << "\n ------------------------------------------------------- \n" << std::endl;
//}
void test_num(int num)
{
u_test_num num_dply;
num_dply.num = num;
std::cout << "No. " << num_dply.num <<"\n" << std::endl;
}
/* ******************************************************************* */
/* 演算例 */
/* ******************************************************************* */
#ifdef UT_EX_1
void test_1(void)
{
u_test_1 ex1;
ex1.r = sqrt(ex1.n); /*平方根*/
/* ========== 負数の平方根: 無効例外(Invalid) を検出 ========== */
/* --------------->> 検出される例外: 無効 ---------- */
std::cout << " Sqrt(" << ex1.n << ") = " << ex1.r << "\n" << std::endl;
}
#endif
#ifdef UT_EX_2
void test_2(void)
{
u_test_2to3 ex2;
ex2.ys = ex2.a;
/* ========== 単精度データに、倍精度データを入力 ========== */
/* --------------->> 検出される例外: 不正確 ---------- */
/* --------------->> 検出される例外: アンダーフロー -- */
std::cout << " Result: ys = " << ex2.ys << "\n" << std::endl;
}
#endif
#ifdef UT_EX_3
void test_3(void)
{
u_test_2to3 ex3;
ex3.yd = ex3.a;
/* ========== 倍精度データに、倍精度データを入力 ========== */
/* --------------->> 検出される例外: なし ---------- */
std::cout << " Result: yd = " << ex3.yd << "\n" << std::endl;
}
#endif
#ifdef UT_EX_4
void test_4(void)
{
u_test_4 ex4;
ex4.b = ex4.y / ex4.x;
/* ==================== ゼロで除算 ========================= */
/* --------------->> 検出される例外: ゼロ除算 -------- */
//std::cout << " Result of (DBL_MIN): dd = " << ex6.dd << "\n" << std::endl;
std::cout << " Result: b = " << ex4.b << "\n" << std::endl;
}
#endif
#ifdef UT_EX_5
void test_5(void)
{
u_test_5 ex5;
ex5.quot; // 商
ex5.numer = 2.0; // 分子
ex5.demonmin = 3.0; // 分母
ex5.quot = ex5.numer / ex5.demonmin;
/* ==================== 割り切れない割り算 ========================= */
/* ------------------>> 検出される例外: 不正確 --------------- */
std::cout << " Result: 2.0/3.0 = " << ex5.quot << "\n" << std::endl;
}
#endif
#ifdef UT_EX_6
void test_6(void)
{
u_test_6to9 ex6;
ex6.dd = DBL_MIN;
/* =============== 倍精度浮動小数点の下限値 DBL_MIN を表示 ========= */
/* --------------->> 検出される例外: なし ------------------ */
std::cout << " Result of (DBL_MIN): dd = " << ex6.dd << "\n" << std::endl;
}
#endif
#if defined UT_EX_7
void test_7(void)
{
u_test_6to9 ex7;
ex7.dd = DBL_MIN * (double) (0.1);
/* =============== 倍精度浮動小数点の下限値 DBL_MIN の 0.1 倍 ============ */
/* --------------->> 検出される例外: アンダーフロー ---------------- */
std::cout << " Result of (DBL_MIN * 0.1): dd = " << ex7.dd << "\n" << std::endl;
}
#endif
#if defined UT_EX_8
void test_8(void)
{
u_test_6to9 ex8;
ex8.dd = DBL_MAX;
/* =============== 倍精度浮動小数点の下限値 DBL_MAX を表示 ========= */
/* --------------->> 検出される例外: なし ------------------ */
std::cout << " Result of (DBL_MAN): dd = " << ex8.dd << "\n" << std::endl;
}
#endif
#if defined UT_EX_9
void test_9(void)
{
u_test_6to9 ex9;
ex9.dd = DBL_MAX * (double) (10.0);
/* =============== 倍精度浮動小数点の上限値 DBL_MAX の 10 倍 ============= */
/* --------------->> 検出される例外: オーバーフロー ---------------- */
std::cout << " Result of (DBL_MIN * 10.0): dd = " << ex9.dd << "\n" << std::endl;
}
#endif
実行結果は、以下の通りです。
注意:
WSL や Visual Studio では 9つの関数を全て 1 個の実行形式ファイルに実装していますが、Nios® V ではメモリ不足から1個の関数だけを実装している為、#ifdef
を使用して、#define
で実装する関数を都度切り替えてコンパイルを行い、各々の実行結果( 9 種類存在することになります。)を、手作業で貼り付けた内容です。
- 実行結果
=========================================================
No. 1
Sqrt(-1) = nan
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
=========================================================
No. 2
Result: ys = 9.99995e-41
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
=========================================================
No. 3
Result: yd = 1e-40
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
=========================================================
No. 4
Result: b = inf
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
=========================================================
No. 5
Result: 2.0/3.0 = 0.666667
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
=========================================================
No. 6
Result of (DBL_MIN): dd = 2.22507e-308
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
=========================================================
No. 7
Result of (DBL_MIN * 0.1): dd = 2.22507e-309
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
=========================================================
No. 8
Result of (DBL_MAN): dd = 1.79769e+308
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
=========================================================
No. 9
Result of (DBL_MIN * 10.0): dd = inf
( No DIVZERO Exception )
( No Inexact Result Exception )
( No Invalid Argument Exception )
( No Overflow Exception )
( No Underflow Exception )
=========================================================
使用した Nios® V のコアには、浮動小数点演算向けに特化した機能ブロックは未実装であるゆえ、浮動小数点例外の検出は行われていないことが把握できます。
5. モデルベース・デザインで浮動小数点例外検出機能の構築
FPGA では HDL 設計でユーザー回路を自在に構築できる設計思想から、浮動小数点例外を検出できる機能の実現に挑戦してみました。筆者が知る限り、Mathworks® 社の MATLAB® は浮動小数点の数値演算に強力な開発ツールと理解しており、MATLAB® 上で動作する Simulink® と連携可能なDSP Builderを使用して、Simulink® モデルベースのデザインで、その機能の構築を試みました。
DSP Builder Advanced Blockset で用意されている専用ブロックを使用すれば、その部分は HDL コードとして生成されるので、手作業でHDLを記述することなく設計できます。
5.1 DSP Builder Advanced Blockset の FloatClass を使用
DSP Builder Advanced Blockset には、FloatClass という機能ブロックが用意されていました。
入力された浮動小数点データが以下の場合に、それぞれに対応したフラグを出力するようです。
- 入力データが、ゼロ
Zer
の場合 - 入力データの符号
Sig
( 正の場合’0’を出力。負の場合’1’を出力 ) - 入力データが、無限大
Inf
の場合 - 入力データが、非数
NaN
の場合
5.2 浮動小数点例外の検出
FloatClass を利用して、例えば除算で Inf
を検出した際、分母の値がゼロの場合はゼロ除算例外とする等の考えをもとにしてモデルを組み込みました。
本稿では、ゼロ除算例外とアンダーフロー例外について掲載します。
5.2.1 アンダーフロー例外の検出
5.2.2 ゼロ除算例外の検出
一応、シミュレーション波形の最後の方では、意図した結果になっています。
しかし、遅延時間の考慮が不十分の為、途中まで波形がバタついています。
この部分は、もう少し精査すべきと感じました。
5.3 HDL コードの生成
Simulink® でのシミュレーションが終了したら、その時点で HDL コードが生成されます。
次に DSP Builder の Run Quartus Prime Software を選択すれば、Quartus® Prime が起動され、同時に FPGA プロジェクトも自動的に構築されるので、Full Compile を実施するだけでデザインが構築されます。
例えば、Nios® V の演算結果や演算オペランドを、この DSP Builder 生成回路に与えて、浮動小数点例外の検出フラグを、Platform Designer の PIO に入力して、Nios® V に割込みを発生させる応用が期待できます。
6. 最後に
浮動小数点例外検出結果の差異ですが、バラつきが当初の予想を超えてしまったようです。掲載したソースコードは 参考程度 と捉えてください。
本稿の執筆を通じて、改めて浮動小数点の奥の深さと実装への難易度を再認識しました。