float_backend API
float_backend 包提供 Complex[Double] 的解析函数:模与辐角、除法、开方、指数与对数、幂,以及三角函数、双曲函数及其反函数。它还为浮点标量定义了三个能力 trait,并为 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 不允许一个包为另一个包的类型添加方法)。
约定
- 主值。 多值函数返回某个固定分支上的一个值,各函数分别说明。
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中止。
能力 trait
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 trait 的组合命名。
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
数值稳定算法在上述两个 trait 之上所用的基本操作。
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 是直接公式,当 时会损失相对精度。本包目前还没有对这些 trait 泛型的公开函数;下面的 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;参见核心 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
缩放避免了平方项的虚假下溢;当 超出 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]
对于有限的 ,它先除以 中较大者(当 时取 ):
因此不会形成 或 的平方。当 没有 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
以实数 作为第二个操作数应用二元复函数。
pub fn op_bin_re(Complex[Double], Double, (Complex[Double], Complex[Double]) -> Complex[Double]) -> Complex[Double]
op_bin_im
以纯虚数 作为第二个操作数应用二元复函数。
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)
}