不能直接用 m_pi 或 3.14159265358979323846,因 m_pi 非标准且编译器支持不一,而 double 字面量仅约17位有效数字;高精度需整数数组模拟大数+ machin 公式(π = 16·arctan(1/5) − 4·arctan(1/239))配合泰勒展开逐位计算。

为什么不能直接用 M_PI 或 3.14159265358979323846
因为 M_PI 是非标准宏,GCC/Clang 默认不定义,MSVC 也不保证存在;而硬编码的 double 字面量最多只有约17位有效数字,超出即失真。真要算到几百、几千位小数,必须脱离浮点数,自己管理每一位数字。
用整数数组模拟大数 + Machin 公式最稳
Machin 公式:π = 16 * arctan(1/5) - 4 * arctan(1/239),收敛快、项数少、整数运算友好。arctan(x) 展开为泰勒级数:arctan(x) = x - x³/3 + x⁵/5 - x⁷/7 + ...,代入 1/5 和 1/239 后,每一步都是整数除法+累加,适合用数组存十进制各位。
实操建议:
- 用
std::vector<int></int>存储结果(每位0–9),索引0为个位,高位在后,方便进位处理 - 每一项计算时,对分子反复乘以
10^k(k为当前所需小数位数)再整除分母,模拟“高精度除法” - 用“长除法逐位生成”比“先算整数再转字符串”更省内存,尤其算万位以上时
- 别用
std::string拼接中间结果——字符串拷贝开销大,且易内存碎片
arctan(1/n) 的整数迭代怎么写才不出错
关键不是公式本身,而是如何避免中间溢出和截断。例如算 arctan(1/5) 第100项:(1/5)^(2*100+1) / (2*100+1),直接算分数会崩。正确做法是边算边缩放:
// 伪代码:计算 arctan(1/n) 的前 N 项,保留 prec 位小数
vector<int> res(prec + 1, 0); // 个位 + 小数部分
vector<int> num(prec + 1, 0); // 当前分子(初始为 1,对应 1/n)
num[0] = 1;
for (int k = 0; k <p>注意:<code>div_by_int</code> 必须从高位往低位做长除法,并处理借位;<code>add_to</code> 要带进位传播;所有操作都得在整数数组上原地完成。</p>
<h3>编译和运行时容易被忽略的坑</h3>
<p>算到10000位以上时,常见问题不是算法错,而是工程细节翻车:</p>
<ul>
<li>没关 <code>-O2</code> 优化可能导致循环被误判为无副作用而删掉——加 <code>volatile</code> 或用 <code>asm volatile("")</code> 插桩验证</li>
<li>用 <code>std::vector::reserve()</code> 预分配空间,否则频繁 <code>push_back</code> 触发多次重分配,性能跌5倍以上</li>
<li>输出时别用 <code>std::cout 逐个刷——改用 <code>fwrite</code> 或预拼 <code>char*</code> 缓冲区,I/O 可快10倍</code>
</li>
<li>Linux 下栈默认只有8MB,<code>std::vector</code> 在栈上声明大数组会段错误——务必堆分配或用 <code>std::unique_ptr<:vector>></:vector></code>
</li>
</ul>
<p>真正卡住人的,往往不是公式推导,而是第3次进位没清零、或除法余数用了有符号类型导致负数截断——这些错不会报编译错误,只会让小数点后第200位开始全偏1。</p></int></int>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











