float_backend API
float_backend パッケージは Complex[Double] の解析関数を提供します。絶対値と偏角、除算、根、指数関数と対数、べき乗、そして三角関数・双曲線関数とその逆関数です。また、浮動小数点スカラー向けに 3 つの能力トレイトを定義し、Float と Double に対して実装しています。式の導出と分岐切断については float_backend の設計 で説明しています。
ソース: src/float_backend/(float_backend_traits.mbt、complex_elementary.mbt、complex_trigonometric.mbt、complex_hyperbolic.mbt)。
インポート
import {
"Luna-Flow/luna-complex/float_backend" @fb,
}
例では、このパッケージに @fb、ルートパッケージに @complex を使います。すべての関数は Complex[Double] 上の自由関数です(MoonBit では、パッケージが他のパッケージの型にメソッドを追加できないため)。
規約
- 主値。 多価関数は、関数ごとに説明する固定された分枝の値を 1 つ返します。
argは負の実軸上を除き の値を返します(下の警告を参照)。 - 特殊値。 NaN と無限大は明記された箇所(
div、atan、atanh、acosh、abs_log、pow)で処理されます。それ以外では通常のDouble算術を通じて伝播し、NaN の部分を生じることがあります。 - 逆数系の関数は極で中断します。
sec、csc、cot、sech、csch、coth、asec、acsc、asech、acsch、acoth、およびpowの指数 はComplex::invを使うため、絶対値がゼロのときにDouble::inv: division by zeroで中断します。
能力トレイト
FloatingAnalyticScalar
解析バックエンドが実スカラーに求める代数的・解析的な能力です。
pub(open) trait FloatingAnalyticScalar : @luna-generic.Field + @luna-generic.Num + Compare + @arithmetic.Constants + @arithmetic.Sqrt + @arithmetic.Exponential + @arithmetic.Logarithmic + @arithmetic.Trigonometric + @arithmetic.InverseTrigonometric + @arithmetic.Hyperbolic + @arithmetic.InverseHyperbolic {
}
pub impl FloatingAnalyticScalar for Float
pub impl FloatingAnalyticScalar for Double
独自のメソッドは持たず、luna-generic と arithmetic のトレイトの合成に名前を付けたものです。
FloatingSpecialValues
IEEE 754 の特殊値とその判定です。
pub(open) trait FloatingSpecialValues {
fn nan() -> Self
fn infinity() -> Self
fn neg_infinity() -> Self
fn is_nan(Self) -> Bool
fn is_inf(Self) -> Bool
fn is_pos_inf(Self) -> Bool
fn is_neg_inf(Self) -> Bool
fn is_negative_zero(Self) -> Bool
}
pub impl FloatingSpecialValues for Float
pub impl FloatingSpecialValues for Double
is_negative_zero(x) は is_neg_inf(1.0 / x) なので、ちょうど のときに真になります。
FloatingBackendScalar
上記 2 つのトレイトに加えて、数値的に安定なアルゴリズムが使うプリミティブです。
pub(open) trait FloatingBackendScalar : FloatingAnalyticScalar + FloatingSpecialValues {
fn from_double(Double) -> Self
fn trunc(Self) -> Self
fn to_int(Self) -> Int
fn hypot(Self, Self) -> Self
fn log1p(Self) -> Self
}
pub impl FloatingBackendScalar for Float
pub impl FloatingBackendScalar for Double
| メソッド | 意味 | Double | Float |
|---|---|---|---|
from_double | Double 定数を変換 | 恒等関数 | Float::from_double |
trunc | ゼロ方向への丸め | Double::trunc | Float::trunc |
to_int | Int へ変換 | Double::to_int | Float::to_int |
hypot | オーバーフローしない | @math.hypot | @math.hypotf |
log1p | @math.log1p | ln(1.0 + x) |
Float の log1p は直接の式であり、 で相対精度が失われます。このパッケージの公開関数で、これらのトレイトについてジェネリックなものはまだありません。以下の Complex[Double] 関数は Double のプリミティブを直接呼び出します。
fn[T : @fb.FloatingSpecialValues] classify(x : T) -> String {
if @fb.FloatingSpecialValues::is_nan(x) {
"nan"
} else if @fb.FloatingSpecialValues::is_inf(x) {
"inf"
} else if @fb.FloatingSpecialValues::is_negative_zero(x) {
"-0"
} else {
"finite"
}
}
test "special values" {
assert_eq(classify(-0.0), "-0")
assert_eq(classify(@double.infinity), "inf")
let nan : Float = @fb.FloatingSpecialValues::nan()
assert_eq(classify(nan), "nan")
assert_eq(@fb.FloatingBackendScalar::hypot(3.0, 4.0), 5.0)
}
再エクスポートされる型
Complex
@fb.Complex は @complex.Complex です。core API を参照してください。
pub using @luna-complex {type Complex}
構築と格納
polar
を構築します。
pub fn polar(Double, Double) -> Complex[Double]
正規化は行いません。負の や任意の実数 も受け付けます。
pack
インターリーブされたバッファ [re0, im0, re1, im1, …] の複素インデックス offset の位置に を書き込みます。
pub fn pack(Complex[Double], Array[Double], Int) -> Unit
native_pack
実部と虚部をインターリーブされたバッファに書き込みます。
pub fn native_pack(Double, Double, Array[Double], Int) -> Unit
バッファの長さは偶数でなければなりません。offset が複素要素の個数(arr.length() / 2)に等しい場合はペアを追加し、そうでなければ要素 offset を上書きします。長さが奇数の場合は native_pack: buffer length must be even で、オフセットが 0..=arr.length() / 2 の範囲外の場合は native_pack: offset out of bounds で中断します。
test "polar and packing" {
let z = @fb.polar(2.0, 0.0)
assert_eq(z, @complex.Complex::new(2.0, 0.0))
let buf : Array[Double] = []
@fb.pack(z, buf, 0)
@fb.native_pack(3.0, 4.0, buf, 1)
@fb.native_pack(5.0, 6.0, buf, 0)
assert_eq(buf, [5.0, 6.0, 3.0, 4.0])
}
絶対値と偏角
abs
hypot を使い、途中でオーバーフローやアンダーフローを起こさずに を返します。
pub fn abs(Complex[Double]) -> Double
abs_sqr
として で計算した を返します。
pub fn abs_sqr(Complex[Double]) -> Double
スケーリングにより 2 乗の見かけ上のアンダーフローを避けますが、 が Double の範囲を超える場合は結果自体が無限大にオーバーフローします。 は に写ります。
abs_log
、 として、 により を返します。
pub fn abs_log(Complex[Double]) -> Double
自体がオーバーフローする場合でも、ゼロでない有限の すべてに対して有限です。abs_log(0) は です。
arg
となる偏角 を返します。
pub fn arg(Complex[Double]) -> Double
| 入力 | 結果 |
|---|---|
| (ゼロの符号はどちらでも) | |
| , | (上の警告を参照) |
| それ以外 | atan2(y, x) |
test "modulus and argument" {
let z = @complex.Complex::new(3.0, 4.0)
assert_eq(@fb.abs(z), 5.0)
assert_eq(@fb.abs_sqr(z), 25.0)
assert_eq(@fb.arg(@complex.Complex::new(0.0, 1.0)), @math.PI / 2.0)
let huge = @complex.Complex::new(1.0e300, 1.0e300)
assert_true(@fb.abs_sqr(huge).is_inf())
assert_true(@fb.abs_log(huge) < 692.0) // ln(sqrt 2 * 1e300) is about 691.1
}
除算
div
Smith のスケーリングと IEEE の特殊値処理を用いて を計算します。
pub fn div(Complex[Double], Complex[Double]) -> Complex[Double]
有限の に対しては、まず の大きい方で割ります( のときは ):
そのため や の 2 乗は計算されません。 に NaN の部分がなく、 に無限大の部分がある場合、結果は無限大の符号から計算されます。有限の は の部分を与え、無限大の は符号パターンの商を与えます。 による除算は無限大または NaN を生じます(中断はしません)。
test "robust division" {
let one = @complex.Complex::new(1.0, 0.0)
let tiny = @complex.Complex::new(1.0e-300, 1.0e-300)
let q = @fb.div(one, tiny)
assert_true(q.re > 4.9e299 && q.im < -4.9e299)
let z = @fb.div(@complex.Complex::new(1.0, 2.0), @complex.Complex::new(@double.infinity, 0.0))
assert_eq(z.re, 0.0)
}
根
sqrt
主値の平方根を返します。 で、分岐切断は負の実軸に沿っています。
pub fn sqrt(Complex[Double]) -> Complex[Double]
スケーリングした形で を計算し、 のときは を、 のときは の符号に応じて を返します。分岐切断上(、)では、ゼロの符号によらず結果は です。sqrt(0) は です。
sqrt_real
実数の平方根を複素数として返します。 では 、 では です。
pub fn sqrt_real(Double) -> Complex[Double]
test "square roots" {
assert_eq(@fb.sqrt(@complex.Complex::new(-3.0, 4.0)), @complex.Complex::new(1.0, 2.0))
assert_eq(@fb.sqrt(@complex.Complex::new(-4.0, 0.0)), @complex.Complex::new(0.0, 2.0))
assert_eq(@fb.sqrt_real(-9.0), @complex.Complex::new(0.0, 3.0))
}
指数関数と対数
exp
を返します。
pub fn exp(Complex[Double]) -> Complex[Double]
を先に計算するため、結果の一部が有限になる場合でも でオーバーフローし、exp(710 + 0i) の虚部は NaN になります()。
log
abs_log と arg を使って を返します。
pub fn log(Complex[Double]) -> Complex[Double]
虚部は arg に従うため、負の実軸上では になります。log(0) は です。
log_10
を返します。
pub fn log_10(Complex[Double]) -> Complex[Double]
log_b
div で除算して を返します。
pub fn log_b(Complex[Double], Complex[Double]) -> Complex[Double]
test "exponential and logarithm" {
let z = @complex.Complex::new(1.0, 2.0)
let back = @fb.exp(@fb.log(z))
assert_true((back.re - 1.0).abs() < 1.0e-12 && (back.im - 2.0).abs() < 1.0e-12)
let l = @fb.log_10(@complex.Complex::new(100.0, 0.0))
assert_true((l.re - 2.0).abs() < 1.0e-15)
}
べき乗
pow
を返します。
pub fn pow(Complex[Double], Complex[Double]) -> Complex[Double]
次のケースが順に試されます:
| ケース | 結果 |
|---|---|
| , | |
| 、 が正の実数 | |
| 、それ以外 | NaN + NaN |
z.inv()( のとき中断するが、最初の行で除外済み) | |
| が実整数で | 二進累乗法。負の指数ではまず の逆数をとる |
| それ以外 | 極形式での |
極形式では と を計算し、 を返します。
pow_real
実数の指数 に対して を返します。ゼロ、整数、極形式のケースは pow と同じです。
pub fn pow_real(Complex[Double], Double) -> Complex[Double]
test "powers" {
let z = @complex.Complex::new(1.0, 2.0)
assert_eq(@fb.pow_real(z, 2.0), @complex.Complex::new(-3.0, 4.0))
let r = @fb.pow(@complex.Complex::new(0.0, 1.0), @complex.Complex::new(0.5, 0.0))
assert_true((r.re - 0.7071067811865476).abs() < 1.0e-15)
assert_true(@fb.pow_real(@complex.Complex::new(0.0, 0.0), -1.0).re.is_nan())
}
スカラーオペランド
op_bin_re
2 番目のオペランドを実数 として 2 項複素関数を適用します。
pub fn op_bin_re(Complex[Double], Double, (Complex[Double], Complex[Double]) -> Complex[Double]) -> Complex[Double]
op_bin_im
2 番目のオペランドを純虚数 として 2 項複素関数を適用します。
pub fn op_bin_im(Complex[Double], Double, (Complex[Double], Complex[Double]) -> Complex[Double]) -> Complex[Double]
test "scalar operands" {
let z = @complex.Complex::new(1.0, 2.0)
assert_eq(@fb.op_bin_re(z, 2.0, @fb.div), @complex.Complex::new(0.5, 1.0))
assert_eq(@fb.op_bin_im(z, 1.0, (a, b) => a * b), @complex.Complex::new(-2.0, 1.0))
}
三角関数
sin
を返します。 ちょうどのときは実数の を返します。
pub fn sin(Complex[Double]) -> Complex[Double]
cos
を返します。 ちょうどのときは実数の を返します。
pub fn cos(Complex[Double]) -> Complex[Double]
tan
を返します。
pub fn tan(Complex[Double]) -> Complex[Double]
では を使い、 では でスケーリングした、オーバーフローせずに へ収束する形を使います(設計を参照)。
sec
を返します。 となる点で中断します。
pub fn sec(Complex[Double]) -> Complex[Double]
csc
を返します。 で中断します。
pub fn csc(Complex[Double]) -> Complex[Double]
cot
を返します。 で中断します。
pub fn cot(Complex[Double]) -> Complex[Double]
test "trigonometric functions" {
let z = @complex.Complex::new(1.0, 1.0)
let s = @fb.sin(z)
let c = @fb.cos(z)
let one = s * s + c * c
assert_true((one.re - 1.0).abs() < 1.0e-15 && one.im.abs() < 1.0e-15)
let t = @fb.tan(@complex.Complex::new(1.0, 100.0))
assert_eq(t.im, 1.0)
}
逆三角関数
asin
主値の逆正弦を返します。分岐切断は実軸上の の外側にあり、 は に入ります。
pub fn asin(Complex[Double]) -> Complex[Double]
実数の入力は asin_real へ、純虚数の入力は へ、いずれかの部分が を超える入力は漸近形 へ、それ以外はすべて Hull–Fairgrieve–Tang アルゴリズムへ送られます(設計を参照)。結果は各部分について奇関数であり、 と の符号がそれぞれ実部と虚部にコピーされます。
asin_real
実数の逆正弦です。 では 、 では ( の符号)、NaN に対しては NaN です。
pub fn asin_real(Double) -> Complex[Double]
acos
逆余弦を返します。分岐切断は実軸上の の外側にあります。
pub fn acos(Complex[Double]) -> Complex[Double]
では、実部は の主値で、虚部は と逆の符号を持ちます。 では虚部は主値ですが、実部は主値の ではなく になります。ここで は の実部です(上の警告を参照)。純虚数の入力は を与え、実数の入力は acos_real へ送られます。
acos_real
実数の逆余弦です。 では 、 では 、 では です。
pub fn acos_real(Double) -> Complex[Double]
atan
主値の逆正接を返します。分岐切断は虚軸上の の外側にあります。
pub fn atan(Complex[Double]) -> Complex[Double]
虚部は で、比が に近いときは log1p で計算します。実部は で、大きな入力に対してはスケーリングし直します。分岐切断上(、)では、実部は の符号を持つ です。無限大の入力は を返します。
asec
を返します。 で中断します。
pub fn asec(Complex[Double]) -> Complex[Double]
asec_real
実数の逆正割です。 では 、 では 、 では です。
pub fn asec_real(Double) -> Complex[Double]
acsc
を返します。 で中断します。
pub fn acsc(Complex[Double]) -> Complex[Double]
acsc_real
実数の逆余割です。 では 、 では です。
pub fn acsc_real(Double) -> Complex[Double]
acot
を返し、 では を返します。
pub fn acot(Complex[Double]) -> Complex[Double]
test "inverse trigonometric functions" {
let z = @complex.Complex::new(1.0, 1.0)
let back = @fb.sin(@fb.asin(z))
assert_true((back.re - 1.0).abs() < 1.0e-10 && (back.im - 1.0).abs() < 1.0e-10)
assert_eq(@fb.asin_real(2.0).re, @math.PI / 2.0)
assert_eq(@fb.acot(@complex.Complex::new(0.0, 0.0)).re, @math.PI / 2.0)
}
双曲線関数
sinh
を返します。
pub fn sinh(Complex[Double]) -> Complex[Double]
cosh
を返します。
pub fn cosh(Complex[Double]) -> Complex[Double]
tanh
を返します。 では でスケーリングした、オーバーフローせずに へ収束する形を使います。
pub fn tanh(Complex[Double]) -> Complex[Double]
sech
を返します。 となる点で中断します。
pub fn sech(Complex[Double]) -> Complex[Double]
csch
を返します。 で中断します。
pub fn csch(Complex[Double]) -> Complex[Double]
coth
を返します。 で中断します。
pub fn coth(Complex[Double]) -> Complex[Double]
逆双曲線関数
asinh
を返します。分岐切断は虚軸上の の外側にあります。
pub fn asinh(Complex[Double]) -> Complex[Double]
実数の入力には実数の asinh を使います。純虚数の入力 は、 では を、それ以外では を与えます。
acosh
を返します。分岐切断は実軸上の より下の部分にあります。
pub fn acosh(Complex[Double]) -> Complex[Double]
実数の入力は acosh_real へ送られます。無限大の部分があると を与えます。
acosh_real
実数の逆双曲線余弦です。 では 、 では 、 では です。
pub fn acosh_real(Double) -> Complex[Double]
atanh
を返します。分岐切断は実軸上の の外側にあります。
pub fn atanh(Complex[Double]) -> Complex[Double]
実部は (ゼロ付近では log1p を使用)、虚部は で、大きな入力では再スケーリングされます。無限大の入力は を返します。
atanh_real
実数の逆双曲線正接です。 では 、 では 、 では です。
pub fn atanh_real(Double) -> Complex[Double]
asech
を返します。 で中断します。
pub fn asech(Complex[Double]) -> Complex[Double]
acsch
を返します。 で中断します。
pub fn acsch(Complex[Double]) -> Complex[Double]
acoth
を返します。 で中断します。
pub fn acoth(Complex[Double]) -> Complex[Double]
test "hyperbolic functions" {
let z = @complex.Complex::new(0.5, 0.25)
let back = @fb.tanh(@fb.atanh(z))
assert_true((back.re - 0.5).abs() < 1.0e-12 && (back.im - 0.25).abs() < 1.0e-12)
let w = @fb.sinh(@fb.asinh(@complex.Complex::new(0.0, 2.0)))
assert_true((w.im - 2.0).abs() < 1.0e-12)
assert_eq(@fb.atanh_real(2.0).im, @math.PI / 2.0)
}