gnuradio 中的浮点运算
floating point arithmetic in gnuradio
我试图理解 GNURadio 中的浮点运算并开始研究他们的测试。测试生成随机浮点输入和随机抽头,然后将所有内容传递给过滤器。稍后它使用一些余量比较预期输出和实际输出。
关于那个有一个神秘的评论margin:
// we use a sloppy error margin because on the x86 architecture,
// our reference implementation is using 80 bit floating point
// arithmetic, while the SSE version is using 32 bit float point
// arithmetic.
我在源代码中找不到任何 80 位算法。谁能帮我?或者只是解释为什么误差幅度取决于抽头尺寸?
注意:根据下面 Marcus Müller 的评论,GNURadio 已经有一段时间没有使用 80 位浮点数了,所以问题中引用的评论充其量是过时的。所以这个答案只说明 80 位和 32 位浮点运算之间的一般差异,不适用于当前的 GNURadio 实现。
您没有显示源代码,所以答案是推测性的。据推测,参考实现使用 long double
,而作者使用的 C++ 实现中的 long double
是使用 Intel 硬件内置的 80 位浮点类型实现的。从任何显式扩展精度算术的角度来看,它看起来不像 80 位代码;它看起来就像是 long double
类型的 C++ 代码。
同时,主要实现是用SSE代码实现的。 SSE 是 Intel 指令子集的名称,其中包括一次处理四个 32 位浮点数的 SIMD(单指令多数据)指令。该代码在外观上很明显;它将在汇编中使用 SSE 指令,在 C++ 中使用引用 SSE 指令的编译器内置函数,或者可能在 C++ 中使用编译器扩展来支持向量类型。
当然,使用32位浮点运算时出现的浮点舍入误差与使用80位浮点运算时出现的浮点舍入误差自然不同,所以不会有预期的结果两种不同的方法计算出来的值是相等的。作者设计了测试以容忍结果的一些差异。
需要注意的是,虽然代码当然是针对 Intel 架构的,但并非所有 C++ 实现都使用 80 位浮点运算来实现 long double
。 C++ 实现可能使用 64 位算法。所以,如果你 运行 使用与作者不同的 C++ 实现的代码,你可能会得到不同的结果。虽然 64 位算术通常比 80 位算术具有更大的舍入误差,但作者为 32 位和 80 位算术之间的差异设置的任何容差很可能也涵盖了 32 位和 64 位算术之间的差异, 因此可能不需要调整。
在 x86+87 上,即使简单地使用 double
你也可以获得 80 位精度的中间结果,因为 FPU 堆栈在内部使用 80 位浮点数。
如果您的代码期望并依赖于 64 位或 32 位浮点数学的舍入,您可能会大吃一惊。
例如,我被 x < y
之类的东西击中了很多次,但是在分配 z = x
之后你可能会得到 z >= y
(所有声明的变量 double
).如果 x
最终被分配到 80 位 FPU 寄存器中,而 z
是内存中真正的 64 位浮点变量,则可能会发生这种情况。
g++ 有一个特定的选项来避免这些问题 (-ffloat-store
),它可以防止使用额外的位(但是会大大降低数学密集型代码的速度)。
我试图理解 GNURadio 中的浮点运算并开始研究他们的测试。测试生成随机浮点输入和随机抽头,然后将所有内容传递给过滤器。稍后它使用一些余量比较预期输出和实际输出。
关于那个有一个神秘的评论margin:
// we use a sloppy error margin because on the x86 architecture,
// our reference implementation is using 80 bit floating point
// arithmetic, while the SSE version is using 32 bit float point
// arithmetic.
我在源代码中找不到任何 80 位算法。谁能帮我?或者只是解释为什么误差幅度取决于抽头尺寸?
注意:根据下面 Marcus Müller 的评论,GNURadio 已经有一段时间没有使用 80 位浮点数了,所以问题中引用的评论充其量是过时的。所以这个答案只说明 80 位和 32 位浮点运算之间的一般差异,不适用于当前的 GNURadio 实现。
您没有显示源代码,所以答案是推测性的。据推测,参考实现使用 long double
,而作者使用的 C++ 实现中的 long double
是使用 Intel 硬件内置的 80 位浮点类型实现的。从任何显式扩展精度算术的角度来看,它看起来不像 80 位代码;它看起来就像是 long double
类型的 C++ 代码。
同时,主要实现是用SSE代码实现的。 SSE 是 Intel 指令子集的名称,其中包括一次处理四个 32 位浮点数的 SIMD(单指令多数据)指令。该代码在外观上很明显;它将在汇编中使用 SSE 指令,在 C++ 中使用引用 SSE 指令的编译器内置函数,或者可能在 C++ 中使用编译器扩展来支持向量类型。
当然,使用32位浮点运算时出现的浮点舍入误差与使用80位浮点运算时出现的浮点舍入误差自然不同,所以不会有预期的结果两种不同的方法计算出来的值是相等的。作者设计了测试以容忍结果的一些差异。
需要注意的是,虽然代码当然是针对 Intel 架构的,但并非所有 C++ 实现都使用 80 位浮点运算来实现 long double
。 C++ 实现可能使用 64 位算法。所以,如果你 运行 使用与作者不同的 C++ 实现的代码,你可能会得到不同的结果。虽然 64 位算术通常比 80 位算术具有更大的舍入误差,但作者为 32 位和 80 位算术之间的差异设置的任何容差很可能也涵盖了 32 位和 64 位算术之间的差异, 因此可能不需要调整。
在 x86+87 上,即使简单地使用 double
你也可以获得 80 位精度的中间结果,因为 FPU 堆栈在内部使用 80 位浮点数。
如果您的代码期望并依赖于 64 位或 32 位浮点数学的舍入,您可能会大吃一惊。
例如,我被 x < y
之类的东西击中了很多次,但是在分配 z = x
之后你可能会得到 z >= y
(所有声明的变量 double
).如果 x
最终被分配到 80 位 FPU 寄存器中,而 z
是内存中真正的 64 位浮点变量,则可能会发生这种情况。
g++ 有一个特定的选项来避免这些问题 (-ffloat-store
),它可以防止使用额外的位(但是会大大降低数学密集型代码的速度)。