[英]Why does fmax(a, b) return the smaller (negative) zero and how to cleanly workaround it?
#include <stdio.h>
#include <math.h>
int main () {
float a = 0.0, b = -0.0;
printf("fmax(%f, %f) = %f\n", a, b, fmax(a, b));
}
I get the following result: 我得到以下结果:
gcc f.c -o f -lm
./f
fmax(0.000000, -0.000000) = -0.000000
This (mis)behavior is not documented in the fmax man page. fmax手册页中未记录此(错误)行为。 Is there a reasonable explanation for it? 对它有合理的解释吗? And is there a clean (concise) workaround? 是否有一个干净(简洁)的解决方法? Also, if both are -0.0, I would like to get -0.0 as the max. 另外,如果两者都是-0.0,我想得到-0.0作为最大值。
The "problem" is that a == b
. “问题”是a == b
。 The sign doesn't matter because mantissa (sign put aside) is purely 0. I get 0x80000000
vs 0
符号无关紧要,因为尾数(标记放在一边)纯粹为0.我得到0x80000000
对0
So fmax
just checks if a < b
or b < a
(depending on the implementation), and both are false, so either answer is a potential match. 所以fmax
只是检查a < b
或b < a
(取决于实现),并且两者都是假的,所以答案是一个潜在的匹配。
On my gcc version I get fmax(0.0,-0.0)
at 0.0
, but fmax(-0.0,0.0)
is -0.0
. 在我的gcc版本中,我得到fmax(0.0,-0.0)
为0.0
,但fmax(-0.0,0.0)
为-0.0
。
My attempt at a full workaround, using memcmp
to compare data binary wise in the case of a 0 result. 我尝试了一个完整的解决方法,使用memcmp
在0结果的情况下以二进制方式比较数据。
Even better as suggested, using signbit
which tests if number has negative bit set (regardless of the value): 建议更好,使用signbit
测试数字是否设置负位(无论值如何):
#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\n", a,b, my_fmax(a, b));
a = 0.0;
printf("fmax(%f, %f) = %f\n", a,b, my_fmax(a, b));
a = b = -0.0;
printf("fmax(%f, %f) = %f\n", a,b, my_fmax(a, b));
a = 1.0;
printf("fmax(%f, %f) = %f\n", a,b, my_fmax(a, b));
a = -1.0;
printf("fmax(%f, %f) = %f\n", a,b, my_fmax(a, b));
b = 0.0;
printf("fmax(%f, %f) = %f\n", a,b, my_fmax(a, b));
}
result (I think I covered all the cases): 结果(我想我涵盖了所有案例):
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
From fmax cppreference : 从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. 此函数不需要对零符号敏感,尽管某些实现另外强制执行如果一个参数是+0而另一个是-0,则返回+0。
So I suppose it's up to you to specially handle the case that -0.0
is returned. 所以我想你应该专门处理返回-0.0
的情况。
It's life I'm afraid. 这是我害怕的生活。 IEEE754 allows either -0.0 or +0.0 to be returned. IEEE754允许返回-0.0或+0.0。
(Forgive me for assuming your implementation uses that scheme for floating point.) (请原谅我假设您的实现使用该方案进行浮点。)
More often than not, the first argument will be returned if the two values are equal. 通常,如果两个值相等,则返回第一个参数。 That could form the basis of a workaround, but it is not strictly portable. 这可能是解决方法的基础,但它不是严格可移植的。
You can use the C99 function signbit to distinguish a negative from a positive zero. 您可以使用C99函数signbit来区分负数和正数零。
Why does fmax(a, b) return the smaller (negative) zero 为什么fmax(a,b)返回较小的(负)零
fmax()
compares values . fmax()
比较值 。 +0.0 and -0.0 have the same value . +0.0和-0.0具有相同的值 。 Returning a
or b
meets the fmax()
spec. 返回a
或b
符合fmax()
规范。 A spec footnote specifically addressing this: 专门针对此问题的规范脚注:
Ideally,
fmax
would be sensitive to the sign of zero, for examplefmax(−0. 0, +0. 0)
would return+0
; 理想情况下,fmax
对零符号敏感,例如fmax(−0. 0, +0. 0)
将返回+0
; however, implementation in software might be impractical. 但是,在软件中实施可能是不切实际的。 C11 #361 C11#361
how to cleanly workaround it? 如何干净地解决它?
Use signbit()
to distinguish +0.0
from -0.0
. 使用signbit()
将+0.0
与-0.0
区分开来。 Other ways to distinguish +/-0.0 其他区分+/- 0.0的方法
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 当且仅当其参数值的符号为负时,signbit
宏才返回非零值C1dr§7.12.3.63
In addition to signed zeros, many floating point implementations allow de-normal or not-a-numbers (NaN). 除了带符号的零之外,许多浮点实现还允许非正常或非数字 (NaN)。 In such cases, the usual preferred action is to return the "normal" number if any. 在这种情况下,通常的首选操作是返回“正常”号码(如果有的话)。
With > < >= <=
, the result is false if at least one of the operands are NaN. 如果> < >= <=
,如果至少有一个操作数是NaN,则结果为false。
a > b
is not the opposite of a <= b
. a > b
与a <= b
不相反。 Both could be false. 两者都可能是假的。
Combing this with OP's zero compare goal of +0.0 beats -0.0: 将此与OP的零比较目标+0.0 beats -0.0相结合:
#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;
}
Or perhaps simply detect the special condition else use fmaxf()
- similar to @Jean-François Fabre . 或者只是简单地检测特殊情况,否则使用fmaxf()
- 类似于@ Jean-FrançoisFabre 。 Note: use fmaxf()
for maximum float
. 注意:使用fmaxf()
表示最大float
。
float fmaxf_sz(float a,float b){
if(a==0.0 && b==0.0) {
return signbit(a) ? b : a;
}
return fmaxf(a,b);
}
Here's a version of fmaxf()
that looks at the signbit()
in tie-breaking situations. 这是fmaxf()
的一个版本,它在打破平局的情况下查看signbit()
。
#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\n");
return 1;
}
printf("\n");
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\n",errors);
}
return 0;
}
NB 1: Also note that if you are using float
the best practice is to put f
on the suffix or they will be interpreted as double
. 注意1:另请注意,如果您使用float
,最佳做法是将f
放在后缀上,否则它们将被解释为double
。
I'll leave fminfs
and a type generic macro as an exercise. 我将把fminfs
和类型通用宏作为练习。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.