QuadMathモジュール
主にLinuxで使える4倍精度浮動小数点ライブラリであるquadmathをラップしたRuby Gemを提供します。
ずいぶん前から作っていて、晴れてGemにできた次第です。
以下でインストールできます。
$ gem install quadmath
注意: Windowsなどquadmathが収録されていないOSでは使えないと思います。
使い方
以下でrequireします。
require 'quadmath'
quadmathの数学関数をQuadMathモジュール、内部で使用するプリミティブである__float128をFloat128クラス、__complex128をComplex128クラスとしてオブジェクト化しています。
四則演算などを実装しており、プリミティブ型は数値計算ができます。
Float128('5') + Float128('6') # => 11.0
Complex128('1+1i') + Complex128('2+2i') # => (3.0+3.0i)
また、Rubyのプリミティブ型を使用して算術演算も実装しています。
Float128('5') + 1 # => 6.0
Complex128('2+1i') * 2 # => (4.0+2.0i)
数値型はRubyの仕様に合わせて(Rationalクラスなら#to_r
というように)相互に変換できるよう実装しています。また、Rubyの数値プリミティブ型から四倍精度にする#to_f128
、#to_c128
もあります。各々Float128型とComplex128型に対応します。
1.to_f128 # => 1.0
1.to_c128 #=> (1.0+0.0i)
Rationalでも相互変換を実装しています。内部で四倍精度として扱い、二倍精度における情報落ちはしないので有用です。
(1/3r).to_f128 # => 0.3333333333333333333333333333333333
二倍精度から四倍精度への変換にも対応しています。ただし(案の定)情報落ちします。
(1/3r).to_f.to_f128 #=> 0.333333333333333314829616256247391```
Complex型とComplex128型を計算する場合、返却値はFloat128型をメンバーとして持つComplex型になることには注意が必要です。
c = Complex128('1+2i') + Complex('2+2i') #=> (3.0+4.0i)
c.class # => Complex
Complex128型は(Complex型と同様に)比較できません。
Complex128::I == Complex::I # => false
QuadMathモジュールはFloat128型とComplex128型を組み合わせています。
引数の型によって使用するライブラリ関数が異なる仕様です。例えば、#sqrt
の引数がFloat128の場合はsqrtq()
を使い、Complex128の場合はcsqrtq()
を使います。
QuadMath.sqrt(Float128('1')) # => 1.0
QuadMath.sqrt(Complex128('1')) # => (1.0+0.0i)
関数に分岐切断線(ブランチカット)がある場合、実数領域外の複素解として解釈します。
QuadMath.sqrt(-1) # => (0.0+1.0i)
仕様
Rubyの実装に合わせてライブラリ関数をラップしています。例えば#inspect
はquadmath_snprintf()
を用いたool_quad2str()
という関数で文字列変換します。
QuadMath.sqrt(2) # => 1.4142135623730950488016887242096981
ただ、精度の面で正確性に難があります。このあたりの改善方法があれば誰か教えてください。
Float128('8') # => 8.0000000000000000000000000000000004
#to_s
もこの実装です。Rubyの記法に基づきます。
quadmath_snprintf()
単体は#quadmath_sprintf
としてフロントエンドを用意しています。ただし、メソッド名がやや異なるので注意してください。
quadmath_sprintf("%Qf", 2) # => "2.000000"
quadmath_sprintf("%Qf", 7/10r) # => "0.700000"
quadmath_sprintf("%.*Qf", Float128::DIG, 1/3r) # => "0.333333333333333333333333333333333"
quadmath_sprintf("%.*Qf", Float128::DIG, 1.0/3.0) # => "0.333333333333333314829616256247391"
quadmath_sprintf("%.*Qf", Float128::DIG, 1.to_f128 / 3) # => "0.333333333333333333333333333333333"
width = 46; prec = 20;
quadmath_sprintf("%+-#*.*Qe", width, prec, QuadMath.sqrt(2)) # => "+1.41421356237309504880e+00 "
プリミティブ型のインスタンス生成には、Float128型はFloat128()
、Complex型はComplex128()
を使用し、各々strtoflt128()
をラップしています。
記法はRubyに基づきます。
Float128('1.0') # => 1.0
Float128('0x2.0p+3') # => 16.0 # 16進にも対応
Complex128('1+1i') # => (1.0+1.0i)
Complex128('2e1+3e1i') # => (20.0+30.0i)
strtoflt128()
単体もそのままフロントエンドを用意しています。
第二引数はオプションsp
として扱っています。
strtoflt128('inf') # => Infinity
strtoflt128('-1') # => -1.0
strtoflt128('0xdeadbeef') # => 3735928559.0
sp = '' # => ""
strtoflt128('0+1i', sp: sp) # => 0.0
sp # => "+1i"
#floor
#ceil
#round
#truncate
は各々ライブラリ関数をFloat128型のメソッドに落とし込んでいます。
Float128('8.5').floor # => 8
Float128('8.5').ceil # => 9
Float128('8.5').round # => 9
Float128('8.5').truncate # => 8
nextafterq()
は#next_float
や#prev_float
の実装に使っています。
f128 = Float128(1.0) # => 1.0
f128.next_float # => 1.0000000000000000000000000000000002
f128.prev_float # => 0.9999999999999999999999999999999999
その他の実装については、ソースコードのext/quadmath
を参照してください。
リスト
以下にリストを示します。
Float128 クラスのラップされた定数のリスト:
QuadMath定数 | ライブラリ |
---|---|
NAN | nanq() |
INFINITY | HUGE_VALQ |
MAX | FLT128_MAX |
MIN | FLT128_MIN |
EPSILON | FLT128_EPSILON |
DENORM_MIN | FLT128_DENORM_MIN |
MANT_DIG | FLT128_MANT_DIG |
MIN_EXP | FLT128_MIN_EXP |
MAX_EXP | FLT128_MAX_EXP |
DIG | FLT128_DIG |
MIN_10_EXP | FLT128_MIN_10_EXP |
MAX_10_EXP | FLT128_MAX_10_EXP |
QuadMathモジュール内のラップされた関数のリスト:
メソッド名 | 実数解の関数 | 複素数解の関数 |
---|---|---|
:exp | expq() | cexpq() |
:exp2 | exp2q() | --- |
:expm1 | expm1q() | --- |
:log | logq() | clogq() |
:log2 | log2q() | --- |
:log10 | log10q() | clog10q() |
:log1p | log1pq() | --- |
:sqrt | sqrtq() | csqrtq() |
:sqrt3 | cbrtq() | --- |
:cbrt | cbrtq() | --- |
:sin | sinq() | csinq() |
:cos | cosq() | ccosq() |
:tan | tanq() | ctanq() |
:asin | asinq() | casinq() |
:acos | acosq() | cacosq() |
:atan | atanq() | catanq() |
:atan2 | atan2() | --- |
:quadrant | atan2() | --- |
:sinh | sinhq() | csinhq() |
:cosh | coshq() | ccoshq() |
:tanh | tanhq() | ctanhq() |
:asinh | asinhq() | casinhq() |
:acosh | acoshq() | cacoshq() |
:atanh | atanhq() | catanhq() |
:hypot | hypotq() | --- |
:erf | erfq() | --- |
:erfc | erfcq() | --- |
:lgamma | lgammaq() | --- |
:lgamma_r | lgammaq() | --- |
:signgam | lgammaq() | --- |
:gamma | tgammaq() | --- |
:j0 | j0q() | --- |
:j1 | j1q() | --- |
:jn | jnq() | --- |
:y0 | y0q() | --- |
:y1 | y1q() | --- |
:yn | ynq() | --- |
QuadMathモジュール内のラップされた定数のリスト
QuadMathの定数 | ライブラリの定数 |
---|---|
E | M_Eq |
LOG2E | M_LOG2Eq |
LOG10E | M_LOG10Eq |
LN2 | M_LN2q |
LN10 | M_LN10q |
PI | M_PIq |
PI_2 | M_PI_2q |
PI_4 | M_PI_4q |
ONE_PI | M_1_PIq |
TWO_PI | M_2_PIq |
TWO_SQRTPI | M_2_SQRTPIq |
SQRT2 | M_SQRT2q |
SQRT1_2 | M_SQRT1_2q |
関連サイト
gcc.gnu.org/onlinedocs/libquadmath/
https://www.github.com/tribusonz-2/ruby-quadmath.git