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