如何正确设置std::complex<mpf_class>的精度并完成输入?
问题成因
两类异常表现的核心原因都是C++标准库提供的std::complex通用流提取运算符未针对GMP的mpf_class做特化适配,具体对应两种场景的逻辑如下:
- 先声明复数、再设置精度、最后读取出现截断:
std::complex<mpf_class>默认构造时,内部的实部、虚部mpf_class实例会使用GMP默认精度(通常对应十进制20位左右)初始化。标准库的通用operator>>实现会先创建默认精度的临时mpf_class解析输入内容,再赋值给复数内部成员,解析阶段就已经发生精度截断,后续修改内部成员精度不会修正已经截断的结果。 - 先设置精度、再声明复数、最后读取出现非预期舍入:虽然此时复数内部实部、虚部的精度已经是400位,但通用
std::complex流提取实现会先把输入的数字字符串解析为原生double类型,再转换为mpf_class赋值给内部成员。double本身只有约15-17位十进制有效精度,且0.1这类十进制小数无法用二进制浮点数精确表示,转成400位高精度类型时就会暴露底层的浮点误差,出现末尾多余的非预期数字。
你第二种单独读取实部虚部再初始化复数的写法可以正常工作,是因为直接调用了mpf_class自己实现的流提取运算符,会直接解析字符串到高精度类型,没有中间精度损失。
解决方案
1. 直接创建指定精度的std::complex<mpf_class>
可以封装一个工厂函数,实现一行代码创建指定精度的高精度复数:
#include <complex> #include <gmpxx.h> std::complex<mpf_class> make_mpf_complex(mp_bitcnt_t precision, double real = 0.0, double imag = 0.0) { return {mpf_class(real, precision), mpf_class(imag, precision)}; }
使用时直接调用即可得到内部成员精度符合要求的复数:
// 直接创建400位精度的复数 auto c = make_mpf_complex(400);
2. 自定义流提取重载实现便捷输入
只要为std::complex<mpf_class>特化流提取运算符,直接复用mpf_class的原生解析逻辑,就可以像原生复数一样直接读取括号格式的输入,没有中间精度损失:
#include <istream> #include <complex> #include <gmpxx.h> std::istream& operator>>(std::istream& is, std::complex<mpf_class>& num) { char bracket, comma; // 读取左括号 if (!(is >> bracket) || bracket != '(') { is.setstate(std::ios::failbit); return is; } // 直接用mpf_class的流解析读取实部 if (!(is >> num.real())) { return is; } // 读取分隔逗号 if (!(is >> comma) || comma != ',') { is.setstate(std::ios::failbit); return is; } // 直接用mpf_class的流解析读取虚部 if (!(is >> num.imag())) { return is; } // 读取右括号 if (!(is >> bracket) || bracket != ')') { is.setstate(std::ios::failbit); return is; } return is; }
完成上述两步之后,你就可以用非常简洁的写法实现需求:
#include <iostream> #include <iomanip> int main() { auto c = make_mpf_complex(400); std::cin >> c; std::cout << std::setprecision(400) << c << std::endl; return 0; }
输入(0.12345678123456781234567812345678,0)即可得到完全符合预期的输出,无需单独声明实部虚部。
内容的提问来源于stack exchange,提问作者redmoncoreyl
相关产品推荐
相关产品推荐

