float_backend 教程
本教程使用 Complex[Double] 的解析函数进行计算。你将在直角坐标形式与极坐标形式之间转换,求根和对数,解二次方程,计算三角函数及其反函数,并了解分支切割线的位置。公式及其数值处理见 float_backend 设计。
快速开始
moon add Luna-Flow/luna-complex@0.2.0
import {
"Luna-Flow/luna-complex" @complex,
"Luna-Flow/luna-complex/float_backend" @fb,
}
fn main {
let z = @complex.Complex::new(-3.0, 4.0)
println("|z| = \{@fb.abs(z)}")
println("sqrt(z) = \{@fb.sqrt(z)}")
println("exp(z) = \{@fb.exp(z)}")
}
|z| = 5
sqrt(z) = 1 + 2i
exp(z) = -0.032542999640154786 + -0.03767897757486585i
日常任务
极坐标形式
fn main {
let z = @complex.Complex::new(1.0, 1.0)
let r = @fb.abs(z)
let theta = @fb.arg(z)
println("r = \{r}, theta = \{theta}")
println("back: \{@fb.polar(r, theta)}")
}
r = 1.4142135623730951, theta = 0.7853981633974483
back: 1.0000000000000002 + 1i
解二次方程
的根为 。借助 sqrt 和 div,该公式适用于复系数和负判别式:
fn main {
// z^2 + 2z + 5 = 0
let a = @complex.Complex::new(1.0, 0.0)
let b = @complex.Complex::new(2.0, 0.0)
let c = @complex.Complex::new(5.0, 0.0)
let four = @complex.Complex::new(4.0, 0.0)
let two_a = a + a
let root = @fb.sqrt(b * b - four * a * c)
println("z1 = \{@fb.div(-b + root, two_a)}")
println("z2 = \{@fb.div(-b - root, two_a)}")
}
z1 = -1 + 2i
z2 = -1 + -2i
对数与幂
fn main {
let z = @complex.Complex::new(0.0, 1.0)
println("log(i) = \{@fb.log(z)}")
println("i^i = \{@fb.pow(z, z)}")
println("i^2 = \{@fb.pow_real(z, 2.0)}")
println("log10(1000) = \{@fb.log_10(@complex.Complex::new(1000.0, 0.0))}")
}
log(i) = 0 + 1.5707963267948966i
i^i = 0.20787957635076193 + 0i
i^2 = -1 + 0i
log10(1000) = 2.9999999999999996 + 0i
是实数。整数指数使用精确的二进制快速幂,因此 恰好为 。
三角函数及其反函数
fn main {
let z = @complex.Complex::new(0.5, 0.5)
let s = @fb.sin(z)
println("sin(z) = \{s}")
println("asin(sin(z)) = \{@fb.asin(s)}")
println("asin(2) = \{@fb.asin_real(2.0)}")
}
sin(z) = 0.5406126857131534 + 0.4573041531842493i
asin(sin(z)) = 0.5 + 0.4999999999999999i
asin(2) = 1.5707963267948966 + 1.3169578969248166i
asin_real 接受一个 Double,在 之外返回复数结果。
大参数
这些公式经过缩放,因此极大或极小的输入都不会上溢:
fn main {
let huge = @complex.Complex::new(1.0e300, 1.0e300)
println("|huge| = \{@fb.abs(huge)}")
println("log|huge| = \{@fb.abs_log(huge)}")
println("tan(1+50i) = \{@fb.tan(@complex.Complex::new(1.0, 50.0))}")
let q = @fb.div(@complex.Complex::new(1.0, 0.0), @complex.Complex::new(1.0e-300, 1.0e-300))
println("1/(tiny) = \{q}")
}
|huge| = 1.4142135623730952e+300
log|huge| = 691.1221014884936
tan(1+50i) = 6.765311025183565e-44 + 1i
1/(tiny) = 4.9999999999999995e+299 + -4.9999999999999995e+299i
进阶
分支切割线
多值函数在跨越分支切割线时会跳变。sqrt 的割线位于负实轴;从上方和下方逼近它会得到符号相反的根:
fn main {
println(@fb.sqrt(@complex.Complex::new(-4.0, 1.0e-12)))
println(@fb.sqrt(@complex.Complex::new(-4.0, -1.0e-12)))
println(@fb.sqrt(@complex.Complex::new(-4.0, 0.0)))
}
2.5e-13 + 2i
2.5e-13 + -2i
0 + 2i
在割线上,根为 。设计文档列出了每个函数的割线。
编写标量泛型的辅助函数
借助能力 trait,辅助函数可以声明它对实数标量的需求。例如,一个只检查特殊值的函数只要求 FloatingSpecialValues,便可同时适用于 Float 和 Double:
fn[T : @fb.FloatingSpecialValues] finite_parts(re : T, im : T) -> Bool {
!(@fb.FloatingSpecialValues::is_nan(re) || @fb.FloatingSpecialValues::is_inf(re) ||
@fb.FloatingSpecialValues::is_nan(im) || @fb.FloatingSpecialValues::is_inf(im))
}
fn main {
let z = @fb.exp(@complex.Complex::new(800.0, 1.0))
println(finite_parts(z.re, z.im))
println(finite_parts((1.0 : Float), (2.0 : Float)))
}
false
true
常见陷阱
- 负实轴。
arg和log目前在负实轴上返回 而非 (log(-1)为 ),负实数的非整数次幂也继承了这一角度;acos、acos_real、asec_real和acosh_real对负输入也受影响。参见 float_backend API 中的警告。 - 倒数类函数在极点处中止。
cot(0)、csc(0)、asec(0)等会终止程序,而不是返回无穷大。 - 对极端值使用
@fb.div而非/。 核心运算符会计算 ,若其下溢为零则中止。 exp过早上溢。 当 时,exp(z)含有无穷或 NaN 分量。- 仅支持
Complex[Double]。 解析函数不接受Complex[Float]。
后续步骤
- float_backend API 记录了每个函数及其特殊情形。
- float_backend 设计推导了稳定公式,并列出了已知偏差。
- 核心教程介绍泛型算术运算。