使用numpy arange生成浮点数范围时的步长异常问题求助
之前我用numpy.arange生成浮点数范围的时候踩了个实实在在的坑:明明想要从0.8到1.0、步长0.1的序列,结果直接用arange(0.8,1.0,0.1)得到的是[0.8,0.9],少了1.0这个值。于是试着调整结束值,结果又弄巧成拙多出来两个步长,折腾半天没搞定。今天就来分享靠谱的解决办法,而且不用那种先生成整数列表再缩放的路子——毕竟步长可能是0.3这种没法整除10的数。
先看看之前试的代码:
import numpy as np amp_start = 0.8 amp_step = 0.1 amp_end = 1.0 amp_end = (np.ceil(amp_end / amp_step)) * amp_step amp_end_2 = (np.ceil(amp_end / amp_step)) * amp_step + amp_step print(amp_end) field_amp_range = np.arange(amp_start, amp_end, amp_step) field_amp_range_2 = np.arange(amp_start, amp_end_2, amp_step) field_amp_range = field_amp_range.tolist() print(amp_end, amp_end_2, field_amp_range, field_amp_range_2)
问题根源
为啥会出现这种情况?核心是浮点数的精度误差。numpy.arange的逻辑是从起始值开始,不断累加步长,直到下一个值超过结束值就停止。但浮点数在计算机里是二进制存储的,像0.1这种十进制小数没法精确表示,累加的时候误差会慢慢积累,导致本来应该等于1.0的最后一步,可能实际计算出来是1.0000000000000002,超过了设置的amp_end,就被截断了。而你后来调整amp_end的方式又过度补偿,直接多加了一个步长,自然就多出来两个额外的元素。
靠谱解决方案
方案1:用numpy.linspace(优先推荐)
linspace和arange的逻辑不一样,它是直接指定起始值、结束值、要生成的点数,然后均匀分配数值,完全避开了累加浮点数带来的精度问题,不管步长是多少都能精准包含结束值。
代码示例:
import numpy as np amp_start = 0.8 amp_step = 0.1 amp_end = 1.0 # 计算需要的点数:(结束值 - 起始值)/步长 + 1 num_points = int((amp_end - amp_start) / amp_step) + 1 field_amp_range = np.linspace(amp_start, amp_end, num_points).tolist() print(field_amp_range) # 输出: [0.8, 0.9, 1.0]
哪怕步长换成0.3,比如从0.8到1.7,同样适用:
amp_start = 0.8 amp_step = 0.3 amp_end = 1.7 num_points = int((amp_end - amp_start) / amp_step) + 1 field_amp_range = np.linspace(amp_start, amp_end, num_points).tolist() print(field_amp_range) # 输出: [0.8, 1.1, 1.4, 1.7]
方案2:给结束值加极小偏移量
如果你非要用arange,可以给结束值加一个远小于步长的偏移量,比如amp_step * 1e-8,这样既能确保最后一个值被包含进去,又不会多生成额外的步长。
代码示例:
import numpy as np amp_start = 0.8 amp_step = 0.1 amp_end = 1.0 # 加一个极小的偏移量 field_amp_range = np.arange(amp_start, amp_end + amp_step * 1e-8, amp_step).tolist() print(field_amp_range) # 输出: [0.8, 0.9, 1.0]
这个偏移量要足够小,不能超过步长的一半,不然就可能多生成一步。用amp_step * 1e-8基本能覆盖大部分场景。
总结
如果是生成包含起始和结束值的均匀浮点数序列,优先用linspace,逻辑清晰还不会踩精度的坑;要是习惯用arange,就给结束值加个极小偏移量来规避精度问题。两种方法都不用先搞整数再缩放,完美适配各种步长。
内容的提问来源于stack exchange,提问作者U3.1415926

