Why does fmax(a, b) return the smaller (negative) zero and how to cleanly workaround it?
1 2 3 4 5 6 7 8 | #include <stdio.h> #include <math.h> int main () { float a = 0.0, b = -0.0; printf("fmax(%f, %f) = %f ", a, b, fmax(a, b)); } |
我得到以下结果:
1 2 3 | gcc f.c -o f -lm ./f fmax(0.000000, -0.000000) = -0.000000 |
fmax手册页中没有记录此(错误)行为。 有合理的解释吗? 并且有一个干净(简洁)的解决方法吗? 另外,如果两个都是-0.0,我想将-0.0作为最大值。
"问题"是
因此,
在我的gcc版本中,我在
我尝试一个完整的解决方法,在结果为0的情况下,使用
如建议的更好,使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <stdio.h> #include <math.h> #include <string.h> float my_fmax(float a,float b) { float result = fmax(a,b); if ((result==0) && (a==b)) { /* equal values and both zero the only case of potential wrong selection of the negative value. Only in that case, we tamper with the result of fmax, and just return a unless a has negative bit set */ result = signbit(a) ? b : a; } return result; } int main () { float a = -0.0, b = 0.0; printf("fmax(%f, %f) = %f ", a,b, my_fmax(a, b)); a = 0.0; printf("fmax(%f, %f) = %f ", a,b, my_fmax(a, b)); a = b = -0.0; printf("fmax(%f, %f) = %f ", a,b, my_fmax(a, b)); a = 1.0; printf("fmax(%f, %f) = %f ", a,b, my_fmax(a, b)); a = -1.0; printf("fmax(%f, %f) = %f ", a,b, my_fmax(a, b)); b = 0.0; printf("fmax(%f, %f) = %f ", a,b, my_fmax(a, b)); } |
结果(我认为我涵盖了所有情况):
1 2 3 4 5 6 | fmax(-0.000000, 0.000000) = 0.000000 fmax(0.000000, 0.000000) = 0.000000 fmax(-0.000000, -0.000000) = -0.000000 fmax(1.000000, -0.000000) = 1.000000 fmax(-1.000000, -0.000000) = -0.000000 fmax(-1.000000, 0.000000) = 0.000000 |
来自fmax cppreference:
This function is not required to be sensitive to the sign of zero,
although some implementations additionally enforce that if one
argument is +0 and the other is -0, then +0 is returned.
因此,我认为由您来专门处理返回
我担心这是生活。 IEEE754允许返回-0.0或+0.0。
(请原谅我假设您的实现使用该方案作为浮点。)
如果两个值相等,通常会返回第一个参数。这可以构成解决方法的基础,但是严格来说并不是可移植的。
您可以使用C99函数的符号位来区分负数和正数零。
Why does fmax(a, b) return the smaller (negative) zero
Ideally,
fmax would be sensitive to the sign of zero, for examplefmax(?0. 0, +0. 0) would return+0 ; however, implementation in software might be impractical. C11 #361
how to cleanly workaround it?
使用
The
signbit macro returns a nonzero value if and only if the sign of its argument value is negative C1dr §7.12.3.6 3
除了带符号的零外,许多浮点实现还允许使用非正规数或非数字(NaN)。在这种情况下,通常的首选操作是返回"正常"数字(如果有)。
对于
将此与OP的零比较目标+0.0击败-0.0进行比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <math.h> float fmaxf_sz(float a,float b){ if(!(a<b)) return b; // a is known to be less than b, both are normal if(!(b<a)) return a; // b is known to be less than a, both are normal if (a == b) { // a is known to be equal in value to b, both are normal return signbit(a) ? b : a; } // One or both a,b are NaN return isfinite(a) ? a : b; } |
或者也许只是检测特殊情况,否则使用
1 2 3 4 5 6 | float fmaxf_sz(float a,float b){ if(a==0.0 && b==0.0) { return signbit(a) ? b : a; } return fmaxf(a,b); } |
这是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #include <stdio.h> #include <math.h> float fmaxfs(float a,float b){ if(a>b){ return a; } if(b!=a){ return b; } if(signbit(a)==0){ return a; } return b; } int test(float a,float b,float e){ float r=fmaxfs(a,b); printf("fmaxfs(%f, %f) = %f", a, b, r); if(r!=e||signbit(r)!=signbit(e)){ printf(" ERROR "); return 1; } printf(" "); return 0; } int main () { int errors=0; errors+=test(0.0f,-0.0f,0.0f); errors+=test(-0.0f,0.0f,0.0f); errors+=test(-0.0f,-0.0f,-0.0f); errors+=test(-0.7f,-0.8f,-0.7f); errors+=test(987.485f,100.0f,987.485f); errors+=test(987.485f,1000000.0f,1000000.0f); errors+=test(-987.485f,-100.0f,-100.0f); errors+=test(-1.3678f,-19999.6789f,-19999.6789f); if(errors>0){ printf("%d ERRORS ",errors); } return 0; } |
注意1:还请注意,如果使用
我将保留