如果手里拿到的是“幅值 + 相位”,在 Go 里最直接的转换函数就是 cmplx.Rect。它接收两个 float64:第一个是幅值 r,第二个是弧度相位 θ,返回一个 complex128。例如幅值为 2、相位为 π/2 时,结果接近 0+2i。
官方文档:https://pkg.go.dev/math/cmplx#Rect
记住一句话:cmplx.Rect(r, theta) 的第二个参数不是角度数字,而是弧度;如果输入来自“30°、45°”这类业务数据,先换算再调用。
先确认 Rect 的两个参数
极坐标里的复数可以写成 z = r(cos θ + i sin θ)。因此,cmplx.Rect 的职责不是解析字符串,也不是把两个实数拼成复数,而是按照极坐标定义计算实部和虚部:
package main
import (
"fmt"
"math"
"math/cmplx"
)
func main() {
// r 是幅值,theta 必须使用弧度;π/2 对应 90 度。
r := 2.0
theta := math.Pi / 2
z := cmplx.Rect(r, theta)
// real 和 imag 分别读取复数的实部与虚部。
fmt.Printf("z = %.2f%+.2fi\\n", real(z), imag(z))
}
这里的 r 表示到原点的距离,theta 表示从实轴正方向开始测量的相位。相位为 0 时,点落在实轴正半轴;相位为 math.Pi/2 时,点转到虚轴正半轴。由于浮点数无法精确保存所有三角函数结果,输出中的实部可能是非常接近 0 的小数,而不是字面上的 0。
用 Rect 构造复数
在工程代码里,Rect 适合处理“业务层已经得到幅值和相位”的场景,例如信号参数、旋转量或复数算法中间结果。它返回 complex128,后续可以直接参与复数加减乘除,也可以传给 cmplx.Abs、cmplx.Phase 等函数。
package main
import (
"fmt"
"math"
"math/cmplx"
)
func main() {
// 同一个幅值在不同相位下落到复平面的不同方向。
inputs := []struct {
name string
theta float64
}{
{"正实轴", 0},
{"正虚轴", math.Pi / 2},
{"负实轴", math.Pi},
{"负虚轴", -math.Pi / 2},
}
for _, item := range inputs {
// 保持幅值为 2,只改变相位,便于观察方向变化。
z := cmplx.Rect(2, item.theta)
fmt.Printf("%s: %.3f%+.3fi\\n", item.name, real(z), imag(z))
}
}
这个写法的重点是把“幅值”和“方向”分开表达。若业务对象本身就是复数常量,例如明确知道结果是 3+4i,直接写复数更清楚;只有输入天然以极坐标表达时,Rect 才是更贴合业务语义的入口。
处理角度单位和符号
最容易出现的错误是把角度制直接传给 Rect。Go 的三角函数族使用弧度,所以 90 不是 90°,而是 90 弧度。角度转弧度的公式是 degree * math.Pi / 180,建议在变量名上直接写出单位,减少后续维护时的误读。
package main
import (
"fmt"
"math"
"math/cmplx"
)
func main() {
degree := 45.0
// Rect 要求相位用弧度,先把业务输入的角度转换一次。
theta := degree * math.Pi / 180
z := cmplx.Rect(1, theta)
fmt.Printf("%.1f° -> %.6f + %.6fi\\n", degree, real(z), imag(z))
}
相位可以是负数,负相位表示从实轴正方向向下旋转。例如 -math.Pi/2 指向负虚轴。幅值通常按非负数传入,因为“幅值”本身是距离;若把负数传给 Rect,数学上可以等价于把方向再旋转 π,但这会让参数语义变得不直观,不适合作为业务层的默认约定。
当相位来自 math.Atan2(y, x) 时,结果已经是弧度,并且函数会根据 x、y 的符号判断象限。不要再对它做一次角度转换,否则会把单位弄乱。
用 Polar 做结果检查
如果想确认转换是否符合预期,可以用 cmplx.Polar 把结果重新拆成幅值和相位。官方文档说明,返回的相位范围是 [-math.Pi, math.Pi];因此相位跨过边界时,数值看起来可能不同,但代表的是同一个方向。
package main
import (
"fmt"
"math"
"math/cmplx"
)
func main() {
r := 3.0
theta := 5 * math.Pi / 6
// 先从极坐标构造复数,再把它拆回极坐标。
z := cmplx.Rect(r, theta)
roundR, roundTheta := cmplx.Polar(z)
fmt.Printf("原始: r=%.6f theta=%.6f\\n", r, theta)
fmt.Printf("回读: r=%.6f theta=%.6f\\n", roundR, roundTheta)
}
比较时不要用“浮点结果必须逐位相等”的判断。幅值和相位都经过了三角函数计算,通常应该比较误差是否小于业务允许的阈值。若回读相位落在 -π 而原始相位写成 π,这属于边界表示差异,不代表 Rect 转换失败。
Rect、直接写复数和 Polar 怎么选
这三个入口解决的是不同的表达问题,按输入形态选即可:
| 输入或目标 | 更合适的选择 | 原因 |
|---|---|---|
| 已有幅值和弧度相位 | cmplx.Rect | 保留极坐标语义,直接得到 complex128 |
| 已有明确的实部和虚部 | 复数字面量或 complex | 避免无意义地绕一圈三角函数 |
| 需要读取复数的幅值和相位 | cmplx.Polar | 把笛卡尔形式拆回极坐标 |
| 需要根据 x、y 求方向 | math.Atan2(y, x) | 同时利用两个分量判断象限,结果是弧度 |
我在实际代码里通常会把单位写进变量名,例如 phaseRad、angleDeg,并把角度换算集中在输入边界。这样 cmplx.Rect 内部只接收弧度,后面的复数运算就不会反复猜测单位。
常见问题
cmplx.Rect 的相位可以直接传角度吗?
不可以。第二个参数按弧度解释,角度制输入要先乘以 math.Pi / 180。
为什么结果的实部不是精确的 0?
因为浮点数和三角函数计算存在近似误差。应按容差比较,而不是把接近 0 的结果当成逻辑错误。
负幅值能不能传给 Rect?
函数参数类型允许它,但业务层的“幅值”通常应保持非负;如果确实使用带符号半径,要明确它与相位增加 π 的等价关系。
Polar 回读的相位为什么和输入相差 2π?
复数方向按周期 2π 重复,而 cmplx.Polar 会把相位规范到 [-π, π]。先比较方向是否等价,再判断数值误差。
小结:cmplx.Rect 适合把“幅值 + 弧度相位”转换为 complex128。记住弧度单位、把幅值保持为非负、在输入边界完成角度换算,并用 cmplx.Polar 进行容差范围内的回读检查,就能避免极坐标和复数表示混用。