为什么转换 'out of range integer to integer' 会导致 IB,而转换 'out of range floating-point to integer' 会导致 UB?
Why converting 'out of range integer to integer' leads to IB, but converting 'out of range floating-point to integer' leads to UB?
后续问题:
上下文:ISO/IEC 9899:202x (E) 工作草案 - 2020 年 2 月 5 日 C17..C2x N2479(强调已添加):
J.3 Implementation-defined behavior, J.3.5 Integers
— The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (6.3.1.3).
6.3.1.4 Real floating and integer
When a finite value of standard floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.
问题:为什么'out of range integer to integer'转换为IB,而'out of range floating-point to integer'转换为UB? IE。为什么行为不一致(例如两种情况下的 IB)?
更新。用户 P.P 的回答。在 duplicated question:
I doubt it's reasonably answerable. It's mainly because of history, and based on the implementations, behaviours of hardware, etc when C was standardized. So "consistency" wasn't possible/practical (it's not like the committee decided to arbitrarily classify certain behaviours as IB, UB, or unspecified).
从标准的角度来看,是否将某事物归类为实现定义行为和未定义行为的问题取决于是否应要求所有实现都记录与语言语义基本一致的行为,无论成本或实用性。没有必要强制实施以客户认为有用的方式处理操作,因为预计允许以这种方式行事的实施在有或没有授权的情况下都会这样做。因此,将实现可能 100% 始终如一地处理的未定义行为有用的操作描述为比描述为实现定义的操作有时可能无法始终如一地实施要好。
请注意,对于将操作视为具有记录行为的实现,有时可能会产生不明显的成本。例如考虑:
int f1(int x, int y);
int f2(int x, int y, int z);
void test(int x, unsigned char y)
{
short temp = x/(y+1);
if (f1(x,y))
f2(x,y,temp);
}
在转换为 short 的平台上执行时始终没有副作用,或者在允许将超出范围的转换视为未定义行为的实现上,x/(y+1) 的计算和转换为short 可以推迟到调用 f1 之后,如果 f1 returns 为零则完全跳过。然而,这种转换可能会影响转换产生的信号的行为,因此在转换可能产生信号的实现标准下似乎是不允许的。
另一方面,虽然在越界转换的情况下让实现发出信号可能很有用,但此类信号主要在诊断质量被视为比诊断质量更重要的情况下有用。表现。性能更重要的实现如果将转换处理为没有副作用,则可以自由地进行上述优化,并且似乎后一种操作过程在所有平台上都是可行的。
在某些平台上,将 float
转换为 int
的最快方法会陷入困境;如前所述,操作可能陷入陷阱的可能性会使分类为实现定义的行为变得昂贵。虽然不太可能有任何平台在其中处理来自例如的转换是不切实际的。 float
到 short
作为从 float
到 int
的转换,然后是从 int
到 short
的转换,有些平台可能不是最有用的行为(例如,如果一个平台可以在没有额外成本的情况下将这种转换的结果与目标类型的范围挂钩,那么这可能比转换为 int
然后再转换为目标类型更有用).即使该标准的作者期望并打算从浮点类型到小整数类型的转换永远不会为 int
范围内的任何值产生无序陷阱,该标准将其归类为 UB 一般操作,这可能在某些情况下行为不可预测,但在其他情况下以可预测的特定于实现的方式运行,而无需任何努力来确定它们应该以可预测的方式运行的特定情况。
通过检查 C89 和 C99 中描述的左移方式,或许可以最好地说明后一个原则。对于 x
的所有整数值,x << 0
没有理由不产生 x
,而 C89 指定行为的方式恰恰会做到这一点。然而,C89 规范在某些情况下指定了行为,在这些情况下,允许某些实现以不同的、不一定可预测的方式运行可能很有用。 C99 没有努力确定所有实现都应该像 C89 一样处理负数左移的情况,因为作者预计所有实现都将以 C89 的方式处理此类情况,无论是否有强制要求。
后续问题:
上下文:ISO/IEC 9899:202x (E) 工作草案 - 2020 年 2 月 5 日 C17..C2x N2479(强调已添加):
J.3 Implementation-defined behavior, J.3.5 Integers
— The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (6.3.1.3).
6.3.1.4 Real floating and integer
When a finite value of standard floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.
问题:为什么'out of range integer to integer'转换为IB,而'out of range floating-point to integer'转换为UB? IE。为什么行为不一致(例如两种情况下的 IB)?
更新。用户 P.P 的回答。在 duplicated question:
I doubt it's reasonably answerable. It's mainly because of history, and based on the implementations, behaviours of hardware, etc when C was standardized. So "consistency" wasn't possible/practical (it's not like the committee decided to arbitrarily classify certain behaviours as IB, UB, or unspecified).
从标准的角度来看,是否将某事物归类为实现定义行为和未定义行为的问题取决于是否应要求所有实现都记录与语言语义基本一致的行为,无论成本或实用性。没有必要强制实施以客户认为有用的方式处理操作,因为预计允许以这种方式行事的实施在有或没有授权的情况下都会这样做。因此,将实现可能 100% 始终如一地处理的未定义行为有用的操作描述为比描述为实现定义的操作有时可能无法始终如一地实施要好。
请注意,对于将操作视为具有记录行为的实现,有时可能会产生不明显的成本。例如考虑:
int f1(int x, int y);
int f2(int x, int y, int z);
void test(int x, unsigned char y)
{
short temp = x/(y+1);
if (f1(x,y))
f2(x,y,temp);
}
在转换为 short 的平台上执行时始终没有副作用,或者在允许将超出范围的转换视为未定义行为的实现上,x/(y+1) 的计算和转换为short 可以推迟到调用 f1 之后,如果 f1 returns 为零则完全跳过。然而,这种转换可能会影响转换产生的信号的行为,因此在转换可能产生信号的实现标准下似乎是不允许的。
另一方面,虽然在越界转换的情况下让实现发出信号可能很有用,但此类信号主要在诊断质量被视为比诊断质量更重要的情况下有用。表现。性能更重要的实现如果将转换处理为没有副作用,则可以自由地进行上述优化,并且似乎后一种操作过程在所有平台上都是可行的。
在某些平台上,将 float
转换为 int
的最快方法会陷入困境;如前所述,操作可能陷入陷阱的可能性会使分类为实现定义的行为变得昂贵。虽然不太可能有任何平台在其中处理来自例如的转换是不切实际的。 float
到 short
作为从 float
到 int
的转换,然后是从 int
到 short
的转换,有些平台可能不是最有用的行为(例如,如果一个平台可以在没有额外成本的情况下将这种转换的结果与目标类型的范围挂钩,那么这可能比转换为 int
然后再转换为目标类型更有用).即使该标准的作者期望并打算从浮点类型到小整数类型的转换永远不会为 int
范围内的任何值产生无序陷阱,该标准将其归类为 UB 一般操作,这可能在某些情况下行为不可预测,但在其他情况下以可预测的特定于实现的方式运行,而无需任何努力来确定它们应该以可预测的方式运行的特定情况。
通过检查 C89 和 C99 中描述的左移方式,或许可以最好地说明后一个原则。对于 x
的所有整数值,x << 0
没有理由不产生 x
,而 C89 指定行为的方式恰恰会做到这一点。然而,C89 规范在某些情况下指定了行为,在这些情况下,允许某些实现以不同的、不一定可预测的方式运行可能很有用。 C99 没有努力确定所有实现都应该像 C89 一样处理负数左移的情况,因为作者预计所有实现都将以 C89 的方式处理此类情况,无论是否有强制要求。