使用浮点数和比较作为错误代码是否可移植?

Is using floats and comparison as error code portable?

我计划使用特殊的浮点值来重新运行函数中的错误代码,例如 99.75f 可以在 IEEE-754 中表示:

float myFunction(void)
{
    /* Do some stuff */
    if(error) return 99.75f;
}

然后像这样检查值:

float result = myFunction();
if(result == 99.75f) printf("Error !");

假设我的编译器符合 C90 或 C99 标准,此解决方案是否可移植?它适用于任何平台吗? 或者编译器是否允许做一些魔术或一些优化,将浮点数转换为双精度数,更改其表示形式等,这可能会破坏相等性检查?

著名的 What Every Computer Scientist Should Know About Floating-Point Arithmetic 似乎没有涵盖这种情况(特定于 C)。 与 FLT_EPSILON 或 ULP 进行比较是无关紧要的,因为我确实想检查浮点数的表示...

C 标准对 floating-point 行为的保证不多。它确实说:

All floating constants of the same source form shall convert to the same internal format with the same value.

这意味着 99.75f 将产生相同的值,无论它出现在程序中的任何位置。它不保证 99.750f 将转换为相同的值,因此您必须确保每次出现时都使用完全相同的源文本。它也不保证其他源文本(例如 99.76f)不会转换为相同的值。因此,您需要确保您使用的特殊值与计算中出现的任何值完全分开。

C 标准不保证 floating-point 系统的基数是 2 或 10。因此,仅依靠标准,您不能确定 99.75 是否可表示。当然,你不太可能遇到使用base 3的实现。

C 标准允许实现以额外的范围和精度计算表达式。然而,一旦 99.75f 被转换为 floating-point,仅仅给它额外的范围和精度不会改变它。 +-*/ 和库例程等操作可能会产生近似结果,但据推测,您并不是在对这个值进行操作。

C 标准似乎暗示只有一个基数将用于 floating-point 类型,至少对于普通类型,因为它仅定义 FLT_RADIX 来报告用于floating-point 系统但定义了 FLT_MANT_DIGDBL_MANT_DIG 来报告 floatdouble 的有效数字中使用的位数。因此,您不必担心 99.75 在从 float 转换为 double 或扩展精度时会发生变化。它应该只用相同基数中的更多数字表示,而不是转换为新的基数,因此额外的数字将为零。 (当然,缩小转换可能会改变它,所以你会想要根据 float 定义特殊值,这样它只能变得更宽。)

总的来说,我希望你在这方面相当安全。然而,它不一定是好的设计。当然,特殊值应该只在源文本中指定一次,通过使用预处理器宏或定义为常量值的标识符。