🍒

JuliaとFortranのmodが違う話

2022/04/22に公開

昔, 言語によってmodの(特に負の引数での)挙動が違って苦労した経験があり, プログラムを他言語に移植する際は必ずチェックするようにしています. JuliaとFortran(gfortran)では同じ挙動だったのでメモしておきます. 寝ぼけてたのか? やはり負の引数での挙動が違うので, 移植方法をまとめます.

Juliaのmod

JuliaのmodはFortranのmodと違うので注意しましょう.

versioninfo()
Julia Version 1.7.1
Commit ac5cc99908 (2021-12-22 19:35 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Intel(R) Core(TM) i7-4650U CPU @ 1.70GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-12.0.1 (ORCJIT, haswell)
プログラム
for i in -6:6
  println(i, "\t", mod(i,3))
end
出力
-6	0
-5	1
-4	2
-3	0
-2	1
-1	2
0	0
1	1
2	2
3	0
4	1
5	2
6	0

Juliaのrem

JuliaのremはFortranのmodに相当します.

rem
for i in -6:6
  println(i, "\t", rem(i,3))
end
出力
-6	0
-5	-2
-4	-1
-3	0
-2	-2
-1	-1
0	0
1	1
2	2
3	0
4	1
5	2
6	0

Fortran

見ての通り, Juliaのmod(i,3)とは負の引数での挙動が違います. Fortranのmod(i,3)はJuliaのrem(i,3)と同じです.

gfortran -v
Using built-in specs.
COLLECT_GCC=C:\mingw-w64\mingw64\bin\gfortran.exe
COLLECT_LTO_WRAPPER=C:/mingw-w64/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/8.1.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-8.1.0/configure --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysroot=/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64 --enable-shared --enable-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdcxx-time=yes --enable-threads=posix --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=nocona --with-tune=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-isl=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-pkgversion='x86_64-posix-seh-rev0, Built by MinGW-W64 project' --with-bugurl=https://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' CPPFLAGS=' -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/lib -L/c/mingw810/prerequisites/x86_64-zlib-static/lib -L/c/mingw810/prerequisites/x86_64-w64-mingw32-static/lib '
Thread model: posix
プログラム
program main
  implicit none
  integer :: i

  do i = -6,6
    print *, i, mod(i,3)
  end do

end program main
出力
          -6           0
          -5          -2
          -4          -1
          -3           0
          -2          -2
          -1          -1
           0           0
           1           1
           2           2
           3           0
           4           1
           5           2
           6           0

FortranからJuliaへの移植

古いFortranのコードをJuliaに書き直すことが多いと思いますので, 負の値の挙動について気を付けましょう. if文を使うと場所を取るので, 三項演算子で負の値を判定しています. Fortranのmod(i,3)をJuliaではi<0 ? -mod(-i,3) : mod(i,3)またはrem(i,3)のように書き換えます. 簡単なので単にrem(i,3)を用いることを推奨します.

Fortranと同じ出力を得るコード
for i in -6:6
  println(i, "\t", i<0 ? -mod(-i,3) : mod(i,3))
end
出力
-6	0
-5	-2
-4	-1
-3	0
-2	-2
-1	-1
0	0
1	1
2	2
3	0
4	1
5	2
6	0

JuliaからFortranへの移植

並列計算とかスパコンでの利用のためにJuliaのコードをFortranに書き直すことがたまにあるので, 一応書いておきます. Juliaのmod(i,3)をFortranではmod(abs(i+3),3)とします. 正の引数では変化せず, 負の引数のみ変化します.

Juliaと同じ出力を得るコード
program main
  implicit none
  integer :: i

  do i = -6,6
    print *, i, mod(abs(i+3),3)
  end do

end program main
出力
          -6           0
          -5           2
          -4           1
          -3           0
          -2           1
          -1           2
           0           0
           1           1
           2           2
           3           0
           4           1
           5           2
           6           0

ソースコード

Gistにファイルとして置いておきます.
https://gist.github.com/ohno/2dc6023082659a315577fdb86aa4627c

Discussion