本文介绍了下溢何时发生?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我进入计算 1.77e-308/10 触发下溢异常,但计算 1.777e-308/10 不。这很奇怪,因为: lockquote 当浮点操作的真实结果幅度较小时(即,更接近于零)最小值可以作为目标数据类型(来自算术下溢,维基百科)的正常浮点数表示 换句话说,如果我们计算 x / y ,其中 x 和 y 是 double ,那么如果 0 (标准化的最小正数 double 是 2.2251e-308 )。因此理论上, 1.77e-308/10 和 1.777e-308/10 应该触发一个下溢异常。这个理论与我用下面的C程序测试过的东西是矛盾的。 #include< stdio.h> #include< fenv.h> #include< math.h> int main(){ double x,y; // x = 1.77e-308 =>下溢 // x = 1.777e-308给出==>没有下溢 x = 1.77e-308; feclearexcept(FE_ALL_EXCEPT); y = x / 10.0; if(fetestexcept(FE_UNDERFLOW)){ puts(Underflow \\\); } else puts(No underflow \\\); } 为了编译程序,我使用了 gcc程序。 c -lm ;我也试过Clang,这给了我相同的结果。任何解释? 我已经通过这个在线IDE 。 解决方案 $下溢不仅是一个范围的问题,而且还是精度/四舍五入。 b $ b binary64 0x1.98e566222bcfcp-1023,恰好有一个有效数字(0x198E566222BCFC,7193376082541820),它是10的倍数。所以除以10是确切的。没有舍入错误。 我发现这个更容易用十六进制表示来演示。请注意,除以2除以最小值之外总是精确的。 #include< float.h> #include< stdio.h> #include< fenv.h> #include< math.h> $ b $ int uf_test(double x,double denominator){ printf(%。17e%24a,x,x); feclearexcept(FE_ALL_EXCEPT); double y = x / denominator; int uf = !! fetestexcept(FE_UNDERFLOW); printf(% - 24a%s \\\,y,uf?Underflow:); 返回uf; int main(void){ uf_test(DBL_MIN,2.0); uf_test(1.777e-308,2.0); uf_test(1.77e-308,2.0); uf_test(DBL_TRUE_MIN,2.0); uf_test(pow(2.0,-1000),10.0); uf_test(DBL_MIN,10.0); uf_test(1.777e-308,10.0); uf_test(1.77e-308,10.0); uf_test(DBL_TRUE_MIN,10.0); 返回0; $ b $ pre> 2.22507385850720138e-308 0x1p-1022 0x1p-1023 1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.98e566222bcfcp-1024 1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.97490d21e478cp-1024 4.94065645841246544e-324 0x1p-1074 0x0p + 0下溢 //不下溢,因为不精确的结果不是太小 9.33263618503218879e-302 0x1p-1000 0x1.999999999999ap-1004 //由于结果太小而且不精确,导致下溢 2.22507385850720138e-308 0x1p-1022 0x1.99999999999ap-1026下溢 //没有下溢,因为结果是精确 1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.471deb4e8973p-1026 1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.45d40a818394p-1026下溢 4.94065645841246544e-324 0x1p-1074 0x0p + 0下溢 I get into a situation where calculating 1.77e-308/10 triggers an underflow exception, but calculating 1.777e-308/10 does not. This is strange because: In other words, if we calculate x/y where both x and y are double, then underflow should occur if 0 < |x/y| < 2.2251e-308 (the smallest positive normalized double is 2.2251e-308). In theory, therefore, both 1.77e-308/10 and 1.777e-308/10 should trigger an underflow exception. The theory contradicts with what I have tested with the C program below.#include <stdio.h>#include <fenv.h>#include <math.h>int main(){ double x,y; // x = 1.77e-308 => underflow // x = 1.777e-308 gives ==> no underflow x=1.77e-308; feclearexcept(FE_ALL_EXCEPT); y=x/10.0; if (fetestexcept(FE_UNDERFLOW)) { puts("Underflow\n"); } else puts("No underflow\n");}To compile the program, I used gcc program.c -lm; I also tried Clang, which gave me the same result. Any explanation?[Edits] I have shared the code above via this online IDE. 解决方案 Underflow is not only a question of range, but also of precision/rounding.1.777e-308, converted to the nearest binary64 0x1.98e566222bcfcp-1023, happens to have a significand (0x198E566222BCFC, 7193376082541820) that is a multiple of 10. So dividing by 10 is exact. No roundoff error.I find this easier to demo with hex notation. Note that dividing by 2 is always exact, except for the smallest value.#include <float.h>#include <stdio.h>#include <fenv.h>#include <math.h>int uf_test(double x, double denominator){ printf("%.17e %24a ", x, x); feclearexcept(FE_ALL_EXCEPT); double y=x/denominator; int uf = !!fetestexcept(FE_UNDERFLOW); printf("%-24a %s\n", y, uf ? "Underflow" : ""); return uf;}int main(void) { uf_test(DBL_MIN, 2.0); uf_test(1.777e-308, 2.0); uf_test(1.77e-308, 2.0); uf_test(DBL_TRUE_MIN, 2.0); uf_test(pow(2.0, -1000), 10.0); uf_test(DBL_MIN, 10.0); uf_test(1.777e-308, 10.0); uf_test(1.77e-308, 10.0); uf_test(DBL_TRUE_MIN, 10.0); return 0;}Output2.22507385850720138e-308 0x1p-1022 0x1p-10231.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.98e566222bcfcp-10241.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.97490d21e478cp-10244.94065645841246544e-324 0x1p-1074 0x0p+0 Underflow// No underflow as inexact result is not too small9.33263618503218879e-302 0x1p-1000 0x1.999999999999ap-1004// Underflow as result is too small and inexact2.22507385850720138e-308 0x1p-1022 0x1.99999999999ap-1026 Underflow// No underflow as result is exact1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.471deb4e8973p-10261.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.45d40a818394p-1026 Underflow4.94065645841246544e-324 0x1p-1074 0x0p+0 Underflow 这篇关于下溢何时发生?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 06-13 00:12