imo it's better to express the conversion relation as exp-log pairs:
fn exp(q: Quat) -> Quat {
let ijk = sqrt(q.i*q.i + q.j*q.j + q.k*q.k);
Quat {
i: exp(q.l) * sin(ijk) * q.i / ijk,
j: exp(q.l) * sin(ijk) * q.j / ijk,
k: exp(q.l) * sin(ijk) * q.k / ijk,
l: exp(q.l) * cos(ijk),
}
}
fn log(q: Quat) -> Quat {
let ijk = sqrt(q.i*q.i + q.j*q.j + q.k*q.k);
Quat {
i: atan2(ijk, q.l) * q.i / ijk,
j: atan2(ijk, q.l) * q.j / ijk,
k: atan2(ijk, q.l) * q.k / ijk,
l: 0.5 * log(q.i*q.i + q.j*q.j + q.k*q.k + q.l*q.l),
}
}