绘制区间[1e27,1e33]内|sin(x)|/x曲线异常,求解决方案
问题分析与解决建议
问题原因
你遇到的核心问题是双精度浮点数的精度限制:Python默认的浮点数是64位双精度,仅能保留约15-17位有效数字。当x达到1e27时,x的整数部分已经占据27位,双精度无法精确表示x的小数部分——而sin(x)的取值完全依赖于x mod 2π的余数(即x的小数部分模2π的结果)。当x大到1e27及以上时,x的小数部分精度完全丢失,sin(x)的计算结果变成随机噪声,导致曲线异常。
另外你原代码还有一个小问题:plt.plot(abs(np.sin(x))/x)没有传入实际的x值作为x轴数据,默认用数组索引作为x轴,即使设置了对数刻度,也无法正确反映x的指数变化。
解决方案
方案1:利用三角函数周期性,快速生成平滑曲线(适合绘图场景)
基于sin(x) = sin(x mod 2π)的周期性,我们可以通过累积增量的方式,保持x mod 2π的余数连续变化,避免超大数值的精度丢失:
import numpy as np import matplotlib.pyplot as plt # 生成log空间的t值,对应x=10^t t = np.linspace(27, 33, 1000000) x = 10 ** t # 计算每个点的x增量对应的模2π余数变化 dt = t[1] - t[0] dr = (10 ** t) * np.log(10) * dt # dx/dt = 10^t * ln10,dx对应的角度增量dr=dx(因为弧度制下角度=弧长) r = np.cumsum(dr) % (2 * np.pi) # 累积角度并模2π,保持余数在[0,2π)区间 # 计算函数值并绘图 y = np.abs(np.sin(r)) / x plt.plot(x, y) plt.xscale('log') plt.ylabel('|sin(x)|/x') plt.xlabel('x') plt.show()
这个方法通过连续累积角度增量,保证sin(r)的取值是连续平滑的,同时避免了直接计算超大x的sin(x),绘图速度快且曲线符合预期。
方案2:使用任意精度库计算精确值(需要高精度结果时)
如果需要得到精确的函数值,可以用mpmath库设置足够高的精度,直接计算超大x的模2π余数:
import mpmath as mp import numpy as np import matplotlib.pyplot as plt # 设置60位精度,足够处理1e33级别的数值 mp.mp.dps = 60 # 生成log空间的x值(适当减少点数加快计算) t = np.linspace(27, 33, 100000) x = 10 ** t y = [] # 批量处理提升速度 for xi in x: xi_mp = mp.mpf(xi) # 计算精确的x mod 2π余数 r = xi_mp % (2 * mp.pi) y.append(float(abs(mp.sin(r)) / xi_mp)) y = np.array(y) plt.plot(x, y) plt.xscale('log') plt.ylabel('|sin(x)|/x') plt.xlabel('x') plt.show()
这个方法能得到精确的结果,但计算速度较慢,适合点数不多的场景。
内容的提问来源于stack exchange,提问作者danial
相关产品推荐
相关产品推荐

