#pragma STDC FENV_ACCESS のサンプルコード
注釈
exeGCC では未サポートとなります。
注釈
exeClang の AArch64/ARM 版は SOLID-4.0 時点ではまだリリースされておりませんが、ARM は最新安定版となる LLVM 19 でも未サポートという現状です。AArch64 は後述する -frounding-math
オプションも含め、フルサポートする予定です。
以下のサンプルコードは コグノスケ様のブログ記事 を参考にさせていただきました。
#include <fenv.h>
#include <stdio.h>
#ifdef FENV_ON
#pragma STDC FENV_ACCESS ON
#endif
#define BASE 8388610.0f
union n_f {
int n;
float f;
};
static inline void test() {
union n_f a, b, c;
a.f = BASE;
b.f = 0.25f;
c.f = a.f + b.f;
printf(" %.2f+%.2f = %.2f 0x%08x\n", a.f, b.f, c.f, c.n);
b.f = 0.5f;
c.f = a.f + b.f;
printf(" %.2f+%.2f = %.2f 0x%08x\n", a.f, b.f, c.f, c.n);
b.f = 0.75f;
c.f = a.f + b.f;
printf(" %.2f+%.2f = %.2f 0x%08x\n", a.f, b.f, c.f, c.n);
}
int main() {
printf("DOWNWARD\n");
fesetround(FE_DOWNWARD);
test();
printf("TONEAREST\n");
fesetround(FE_TONEAREST);
test();
printf("TOWARDZERO\n");
fesetround(FE_TOWARDZERO);
test();
printf("UPWARD\n");
fesetround(FE_UPWARD);
test();
return 0;
}
以下は MSYS2 上で exeClang(RV64) s008 と QEMU_SOLID(RISCV) を使用した時の実行結果になります。
最適化無しの場合は期待通りに動きますが。
$ source /c/GCC4/RV64/s008/setup-rv64gc.sh
$ clang $CLANG_CFLAGS -Wall -Wextra test_pragma_fenv_access.c $PTHREAD_STUBS $QEMU_LDFLAGS $CLANG_LDFLAGS
$ /c/KMC/QEMU_SOLID/RISCV/qemu/qemu-system-riscv64.exe -M spike -cpu rv64 -m 1G -nographic -nodefaults -semihosting -bios none -kernel a.out
DOWNWARD
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388610.00 0x4b000002
TONEAREST
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388611.00 0x4b000003
TOWARDZERO
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388610.00 0x4b000002
UPWARD
8388610.00+0.25 = 8388611.00 0x4b000003
8388610.00+0.50 = 8388611.00 0x4b000003
8388610.00+0.75 = 8388611.00 0x4b000003
O2 最適化を行うと、コンパイル時に丸めモードを TONEAREST に固定した最適化を行ってしまうため、結果が期待通りになりません。
$ clang $CLANG_CFLAGS -O2 -Wall -Wextra test_pragma_fenv_access.c $PTHREAD_STUBS $QEMU_LDFLAGS $CLANG_LDFLAGS
$ /c/KMC/QEMU_SOLID/RISCV/qemu/qemu-system-riscv64.exe -M spike -cpu rv64 -m 1G -nographic -nodefaults -semihosting -bios none -kernel a.out
DOWNWARD
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388611.00 0x4b000003
TONEAREST
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388611.00 0x4b000003
TOWARDZERO
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388611.00 0x4b000003
UPWARD
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388611.00 0x4b000003
#pragma STDC FENV_ACCESS ON
を有効にすると、以下のように期待通りの結果になります。
$ clang $CLANG_CFLAGS -O2 -DFENV_ON -Wall -Wextra test_pragma_fenv_access.c $PTHREAD_STUBS $QEMU_LDFLAGS $CLANG_LDFLAGS
$ /c/KMC/QEMU_SOLID/RISCV/qemu/qemu-system-riscv64.exe -M spike -cpu rv64 -m 1G -nographic -nodefaults -semihosting -bios none -kernel a.out
DOWNWARD
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388610.00 0x4b000002
TONEAREST
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388611.00 0x4b000003
TOWARDZERO
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388610.00 0x4b000002
UPWARD
8388610.00+0.25 = 8388611.00 0x4b000003
8388610.00+0.50 = 8388611.00 0x4b000003
8388610.00+0.75 = 8388611.00 0x4b000003
本来は -frounding-math
でも同じ効果になるはずですが、LLVM 18.1.8 では期待通り動作しません。
$ clang $CLANG_CFLAGS -O2 -frounding-math -Wall -Wextra test_pragma_fenv_access.c $PTHREAD_STUBS $QEMU_LDFLAGS $CLANG_LDFLAGS
$ /c/KMC/QEMU_SOLID/RISCV/qemu/qemu-system-riscv64.exe -M spike -cpu rv64 -m 1G -nographic -nodefaults -semihosting -bios none -kernel a.out
DOWNWARD
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388610.00 0x4b000002
TONEAREST
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388610.00 0x4b000002
TOWARDZERO
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388610.00 0x4b000002
UPWARD
8388610.00+0.25 = 8388610.00 0x4b000002
8388610.00+0.50 = 8388610.00 0x4b000002
8388610.00+0.75 = 8388610.00 0x4b000002