#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