chu*_*ica 6 c floating-point ieee-754
在调查浮点异常状态标志时,我遇到了一个奇怪的情况,即FE_UNDERFLOW在不期望的情况下设置状态标志.
这类似于何时发生下溢?然而进入可能是C规范问题或FP硬件缺陷的极端情况.
// pseudo code
// s bias_expo implied "mantissa"
w = smallest_normal; // 0 000...001 (1) 000...000
x = w * 2; // 0 000...010 (1) 000...000
y = next_smaller(x); // 0 000...001 (1) 111...111
round_mode(FE_TONEAREST);
clear_status_flags();
z = y/2; // 0 000...001 (1) 000...000
FE_UNDERFLOW is set!?
Run Code Online (Sandbox Code Playgroud)
我没想到FE_UNDERFLOW设置z如上是正常的,不是正常的.
我希望FE_UNDERFLOW当结果较早浮点运算的低于正常与精度的损失.在这种情况下,精度会下降.
我float和我一起尝试了这个long double并且得到了相同的结果.
经过大量调查后,我注意到,__STDC_IEC_559__在没有确定.
问题
如果__STDC_IEC_559__定义了,在这种情况下,下溢的正确状态是什么?
由于缺乏定义,__STDC_IEC_559__我坚持使用" 未定义的实现__STDC_IEC_559__不需要符合这些规范".C11或是否有一些C规范表明此结果不正确?
由于这肯定是我的硬件(处理器)的结果,您的结果可能会有所不同,这将是有趣的.
以下是一些测试代码,用于演示此功能.起初我怀疑是因为FLT_EVAL_METHOD = 2在我的机器上,然后我尝试了类似的代码long double和相同的结果.
// These 2 includes missing in original post, yet in my true test code
#include <float.h>
#include <math.h>
#include <fenv.h>
#include <stdio.h>
#include <stdint.h>
#define N (sizeof excepts/sizeof excepts[0])
void Report_IEC_FP_exception_status_flags(const char *s) {
printf("%s", s);
int excepts[] = { //
FE_DIVBYZERO, FE_INEXACT, FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW, };
const char *excepts_str[N] = { //
"FE_DIVBYZERO", "FE_INEXACT", "FE_INVALID", "FE_OVERFLOW", "FE_UNDERFLOW", };
int excepts_val[N];
for (unsigned i = 0; i < N; i++) {
excepts_val[i] = fetestexcept(excepts[i]);
}
for (unsigned i = 0; i < N; i++) {
if (excepts_val[i]) printf(" %s", excepts_str[i]);
}
printf("\n");
fflush(stdout);
}
#undef N
void test2(float f, int round_mode, const char *name) {
union {
float f;
uint32_t u32;
} x = { .f = f};
printf("x:%+17a %08lX normal:%c round_mode:%d %s\n", //
f, (unsigned long) x.u32, isnormal(f) ? 'Y' : 'n', round_mode, name);
if (feclearexcept(FE_ALL_EXCEPT)) puts("Clear Fail");
Report_IEC_FP_exception_status_flags("Before:");
f /= 2;
Report_IEC_FP_exception_status_flags("After :");
printf("y:%+17a %08lX normal:%c\n\n",
f,(unsigned long) x.u32, isnormal(f) ? 'Y' : 'n');
}
Run Code Online (Sandbox Code Playgroud)
司机
// In same file as above
int main(void) {
#ifdef __STDC_IEC_559__
printf("__STDC_IEC_559__ = %d\n", __STDC_IEC_559__);
#else
printf("__STDC_IEC_559__ = not define\n");
#endif
float f = FLT_MIN;
printf("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD);
printf("FLT_MIN:%+17a\n", f);
f *= 2.0f;
test2(f, FE_TONEAREST, "FE_TONEAREST");
f = nextafterf(f, 0);
test2(f, FE_TONEAREST, "FE_TONEAREST"); // *** problem? ***
f = nextafterf(f, 0);
test2(f, FE_TONEAREST, "FE_TONEAREST");
}
Run Code Online (Sandbox Code Playgroud)
产量
__STDC_IEC_559__ = not define
FLT_EVAL_METHOD = 2
FLT_MIN: +0x1p-126
x: +0x1p-125 01000000 normal:Y round_mode:0 FE_TONEAREST
Before:
After :
y: +0x1p-126 01000000 normal:Y
x: +0x1.fffffep-126 00FFFFFF normal:Y round_mode:0 FE_TONEAREST
Before:
After : FE_INEXACT FE_UNDERFLOW *** Why FE_UNDERFLOW? ***
y: +0x1p-126 00FFFFFF normal:Y *** Result is normal ***
x: +0x1.fffffcp-126 00FFFFFE normal:Y round_mode:0 FE_TONEAREST
Before:
After :
y: +0x1.fffffcp-127 00FFFFFE normal:n
Run Code Online (Sandbox Code Playgroud)
参考
实施说明:
GNU C11(GCC)版本6.4.0(i686-pc-cygwin),由GNU C版本6.4.0,GMP版本6.1.2,MPFR版本3.1.5-p10,MPC版本1.0.3编译,isl版本0.14或0.13
glibc 2.26发布.
Intel Xeon W3530,64位操作系统(Windows 7)
[次要更新]应使用作为32位十六进制数的商的说明性打印y.u32.这不会改变被测功能
// printf("y:%+17a %08lX normal:%c\n\n",
// f,(unsigned long) x.u32, isnormal(f) ? 'Y' : 'n');
union {
float f;
uint32_t u32;
} y = { .f = f};
printf("y:%+17a %08lX normal:%c\n\n",
f,(unsigned long) y.u32, isnormal(f) ? 'Y' : 'n');
// ^^^^^
Run Code Online (Sandbox Code Playgroud)
虽然无意作为自我回答,但来自不同评论者@John Bollinger、@nwellnhof的输入和进一步的研究导致:
\n\n\n当结果不低于正常值时,浮点状态标志 FE_UNDERFLOW 是否可以设置?
\n
是的,在狭窄的情况下 - 当数学答案介于次正常值和正常值之间时。参见下文。
\n“下溢”发生在以下情况:
\n\n\n如果数学结果的量级太小,以至于无法在指定类型的对象中表示数学结果,而没有异常舍入误差,则结果下溢。C11 7.12.1 错误情况的处理
\n
以上z = y/2;是 1) 不精确(由于四舍五入)和 2) 可能被认为“太小”。
数学
\n可以z = y/2;认为要经历两个阶段:划分和舍入。具有无限精度的数学商小于最小正规数FLT_MIN且大于最大次正规数 nextafterf(FLT_MIN,0)。根据舍入模式,最终答案是这两者之一。与FE_TONEAREST,z一起分配FLT_MIN, 一个正常的数字。
规格
\n下面的 C 规范和 IEC 60559 表明
\n\n\n每当结果很小(基本上低于正常或零)并遭受精度损失时,就会引发“下溢”浮点异常。358 C11 \xc2\xa7F.10 7.
\n
\n 358 IEC 60559 允许不同的下溢定义。它们都产生相同的值,但在引发浮点异常时有所不同。
和
\n\n\n允许两个定义来确定“微小”条件:在将无限精确结果舍入到工作精度之前或之后,具有无界指数。
\n754r 的附录 U 建议,只有舍入后的微小和不精确(如精度损失)才是下溢信号的原因。 维基参考
\n
(我的重点)
\n问答
\n\n\n\n
\n- 如果定义了STDC_IEC_559,那么在这种情况下下溢的正确状态是什么?
\n
在这种情况下,可以设置下溢标志或将其保留。要么遵守。不过,有一个偏好是不设置下溢标志。
\n\n\n2 由于缺乏定义的STDC_IEC_559,我坚持“未定义STDC_IEC_559的实现不需要符合这些规范”。C11 或者是否有一些 C 规范表明此结果不正确?
\n
下溢标志的设置不会导致错误。FP 规范允许这种行为。它还允许不设置下溢标志。
\n\n\n3 由于这肯定是我的硬件(处理器)的结果,因此您的结果可能会有所不同,了解这一点很有趣。
\n
在另一个平台上,__STDC_IEC_559__ = not define和FLT_EVAL_METHOD = 0,FE_INEXACT FE_UNDERFLOW都设置了标志,就像上面的 tst 情况一样。该问题适用于float, double, long double.
如果数学答案位于下面的灰色“之间”区域,它将根据其值和舍入模式向下舍入到次正常值double或向上舍入到正常值。double DBL_MIN如果向下舍入,则FE_UNDERFLOW肯定已设置。如果向上舍入,则FE_UNDERFLOW可以根据何时应用“微小”条件的确定来设置或不设置。
| 归档时间: |
|
| 查看次数: |
195 次 |
| 最近记录: |