仅使用有符号int运算的自定义sine函数出现±1偏移错误问题
错误触发原因
- 核心原因是有符号整数除法的截断方向偏差:C++标准规定有符号整数除法向0截断,你在泰勒展开项的连续乘除计算中,负数区间的除法每次都会多截断掉一部分值,误差累积后当s≥0.9时,缩放后的k=900刚好让累积误差跨过±1的阈值,就会出现固定间隔的±1偏移跳变。
- 次要原因是常量缩放的精度丢失:你当前写死s=0.9,pi=314159*0.9/100的浮点结果为2827.431,直接转整数截断为2827,tau同理截断为5654,这部分系统误差和泰勒展开的截断误差叠加,刚好在每间隔约1弧度的位置触发错误。
- 隐藏原因是32位整数溢出:p的最大取值约为2827,计算a7项时需要连续乘6次p,28276远大于32位有符号int的最大值(231-1≈21亿),溢出后的有符号整数属于未定义行为,会进一步放大结果偏差。
修复方案
- 替换所有整数除法为四舍五入实现:将直接
/操作替换为带四舍五入的整数除法,避免向0截断带来的累积误差,实现逻辑为:
long long round_div(long long a, long long b) { return (a >= 0 ? (a + b / 2) : (a - b / 2)) / b; }
- 中间计算改用64位整数:将a3/a5/a7的类型改为long long,避免乘法溢出。
- 优化pi/tau的缩放逻辑:用纯整数运算实现缩放,避免浮点转整数的截断误差。
修复后核心代码示例
long long round_div(long long a, long long b) { return (a >= 0 ? (a + b / 2) : (a - b / 2)) / b; } int sin(int x, int s1, int s2) { long long p = x; long long pi = 314159; long long k = 1000; long long tau = 628318; const long long six = 6; const long long oneHundredTwenty = 120; const long long fiveThousandFourty = 5040; long long ans; long long a3; long long a5; long long a7; const int s = 90; // 用整数缩放代替浮点0.9,避免精度损失 pi = round_div(pi * s, 10000); tau = round_div(tau * s, 10000); k = round_div(k * s, 100); p %= tau; p -= pi; a3 = p; a5 = p; a7 = p; ans = p; a3 = round_div(a3 * p, k); a3 = round_div(a3 * p, k); a3 = round_div(a3, six); a5 = round_div(a5 * p, k); a5 = round_div(a5 * p, k); a5 = round_div(a5 * p, k); a5 = round_div(a5 * p, k); a5 = round_div(a5, oneHundredTwenty); a7 = round_div(a7 * p, k); a7 = round_div(a7 * p, k); a7 = round_div(a7 * p, k); a7 = round_div(a7 * p, k); a7 = round_div(a7 * p, k); a7 = round_div(a7 * p, k); a7 = round_div(a7, fiveThousandFourty); ans -= a3; ans += a5; ans -= a7; return 30 + round_div(ans, 33); }
内容的提问来源于stack exchange,提问作者RawSteak0
相关产品推荐
相关产品推荐

