32位浮点数比较的最低delta值:1E-05f还是1E-06f可行?
背景
我有一套包含240个针对不同抽头数FIR滤波器卷积的单元测试,用于Unity音频滤波器开发——最初的托管代码朴素卷积速度太慢,无法满足实时音频处理需求。这些测试用来验证不同向量化FIR卷积算法的准确性,最终会选一种用Unity Burst转成支持SIMD的原生代码。
我实现的算法包括:
- 朴素卷积:用作基准对比
- 向量化卷积:包含内层、外层、内外层循环版本
- 上述算法的半带版本:
- 遍历所有抽头的1抽头/2模式
- 利用滤波器对称性遍历半数抽头的1抽头/2模式
测试情况
所有测试在delta值为1E-04f时全部通过;降到1E-05f时仍全部通过;但尝试1E-06f时通过率不足50%。
以下是输入信号为0, 1, 2, 3 ... 28, 29, 30, 31时的测试结果:
1E-05f测试示例
index expected actual difference match 0 -2.7051452E-34 -2.7051452E-34 0.000000E+000 True 1 0.00011297482 0.00011297482 0.000000E+000 True 2 0.00022594964 0.00022594964 0.000000E+000 True 3 -0.0010179491 -0.0010179491 0.000000E+000 True 4 -0.0022618477 -0.0022618477 0.000000E+000 True 5 0.0019123415 0.0019123415 0.000000E+000 True 6 0.00608653 0.00608653 0.000000E+000 True 7 -0.0052014478 -0.0052014478 0.000000E+000 True 8 -0.016489426 -0.016489424 1.862645E-009 True 9 0.009599558 0.009599558 0.000000E+000 True 10 0.035688534 0.035688538 3.725290E-009 True 11 -0.026160091 -0.026160091 0.000000E+000 True 12 -0.08800873 -0.08800873 0.000000E+000 True 13 0.16196862 0.16196862 0.000000E+000 True 14 0.91199124 0.9119913 5.960464E-008 True 15 1.97384 1.97384 0.000000E+000 True 16 3.0356889 3.0356886 2.384186E-007 True 17 4.0095997 4.0095997 0.000000E+000 True 18 4.9835114 4.983511 4.768372E-007 True 19 5.9947987 5.994799 4.768372E-007 True 20 7.006087 7.0060863 4.768372E-007 True 21 8.001913 8.001913 0.000000E+000 True 22 8.997738 8.997738 0.000000E+000 True 23 9.998984 9.998981 2.861023E-006 True 24 11.000226 11.000226 0.000000E+000 True 25 12.0001135 12.0001135 0.000000E+000 True 26 13 13 0.000000E+000 True 27 13.999999 14 9.536743E-007 True 28 15.000001 15.000001 0.000000E+000 True 29 15.999998 16 1.907349E-006 True 30 17 17.000002 1.907349E-006 True 31 18.000002 18 1.907349E-006 True
1E-06f测试示例
index expected actual difference match 0 -2.7051452E-34 -2.7051452E-34 0.000000E+000 True 1 0.00011297482 0.00011297482 0.000000E+000 True 2 0.00022594964 0.00022594964 0.000000E+000 True 3 -0.0010179491 -0.0010179491 0.000000E+000 True 4 -0.0022618477 -0.0022618477 0.000000E+000 True 5 0.0019123415 0.0019123415 0.000000E+000 True 6 0.00608653 0.00608653 0.000000E+000 True 7 -0.0052014478 -0.0052014478 0.000000E+000 True 8 -0.016489426 -0.016489424 1.862645E-009 True 9 0.009599558 0.009599558 0.000000E+000 True 10 0.035688534 0.035688538 3.725290E-009 True 11 -0.026160091 -0.026160091 0.000000E+000 True 12 -0.08800873 -0.08800873 0.000000E+000 True 13 0.16196862 0.16196862 0.000000E+000 True 14 0.91199124 0.9119913 5.960464E-008 True 15 1.97384 1.97384 0.000000E+000 True 16 3.0356889 3.0356886 2.384186E-007 True 17 4.0095997 4.0095997 0.000000E+000 True 18 4.9835114 4.983511 4.768372E-007 True 19 5.9947987 5.994799 4.768372E-007 True 20 7.006087 7.0060863 4.768372E-007 True 21 8.001913 8.001913 0.000000E+000 True 22 8.997738 8.997738 0.000000E+000 True 23 9.998984 9.998981 2.861023E-006 False 24 11.000226 11.000226 0.000000E+000 True 25 12.0001135 12.0001135 0.000000E+000 True 26 13 13 0.000000E+000 True 27 13.999999 14 9.536743E-007 True 28 15.000001 15.000001 0.000000E+000 True 29 15.999998 16 1.907349E-006 False 30 17 17.000002 1.907349E-006 False 31 18.000002 18 1.907349E-006 False
我的比较逻辑如下:
const float delta = 1E-05f; var expectedValue = expected[i]; var actualValue = actual[i]; var difference = Math.Abs(expectedValue - actualValue); var failed = difference > delta;
32位浮点数的精度约为6-9位有效数字。
问题
用于32位浮点数比较的最低delta值是否确实为1E-05f,还是1E-06f仍具备可行性?
回答
首先要明确:32位浮点数的精度是相对精度,不是绝对精度——它的误差范围是基于数值本身的有效位数,而非固定的绝对delta。
从测试结果来看:
当数值量级在10左右时(比如index23的9.998984),误差达到了2.86e-6,这已经超过了
1E-06f的阈值,但仍在32位浮点数的合理精度范围内:10左右的数,6位有效数字对应的绝对误差约为1e-5(10 * 1e-6),7位有效数字对应1e-6。而卷积运算涉及大量加法和乘法,误差会累积,最终超过1e-6是完全正常的。1E-05f是更稳妥的选择:它适配了32位浮点数在大部分音频信号量级下的精度表现,且所有测试都能通过。而1E-06f会因为浮点运算的累积误差导致大量测试失败,这并非算法实现错误,而是浮点精度的固有特性。如果你想尝试更严格的阈值,不要用固定绝对delta,改用相对误差比较会更合理,比如:
const float relativeEpsilon = 1E-6f; float maxValue = Math.Max(Math.Abs(expectedValue), Math.Abs(actualValue)); bool failed = difference > maxValue * relativeEpsilon || difference > 1E-7f; // 加最小绝对阈值处理接近0的数这种方式会根据数值大小动态调整误差允许范围,对小数值更严格,对大数值更宽容,更符合浮点精度的特性。但即使如此,在卷积这种多步运算后,相对误差超过1e-6的情况依然可能存在,需要结合实际音频需求判断——音频信号对微小的浮点误差完全不敏感,只要误差远低于人耳可察觉的阈值(一般是1e-4量级),就不会有问题。
总结:1E-05f是更实际、稳妥的选择;1E-06f在纯理论场景下对小数值可行,但对于卷积这种累积误差的运算,会导致大量不必要的测试失败,不建议使用。
内容的提问来源于stack exchange,提问作者aybe

