简体   繁体   English

为什么fmax(a,b)返回较小的(负)零以及如何干净地解决它?

[英]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.我得到0x800000000

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 < bb < 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. 返回ab符合fmax()规范。 A spec footnote specifically addressing this: 专门针对此问题的规范脚注:

Ideally, fmax would be sensitive to the sign of zero, for example fmax(−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 > ba <= 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.

相关问题 在 C++ abs( *a - *b) 中不返回负数的绝对值 - In C++ abs( *a - *b) does not return absolute value of negative number 为什么fgetpos()返回负偏移量? - Why does fgetpos() return a negative offset? 在这种情况下如何使用负零? - How is negative zero used in this context? 为什么memcmp在存在正差时返回负值? - Why does memcmp return a negative value when there is a positive difference? 如何在C中将负零转换为正零? - How to convert negative zero to positive zero in C? 为什么这个阻塞套接字读取返回零并且没有错误? - Why does this blocking socket read return zero and no error? 为什么GCC编译器允许我们声明零长度的字符串(直接)和负长度的字符串(间接)? - Why does the GCC Compiler allows us to declare zero length strings (directly) and negative length strings (indirectly)? 如何根据给定的 integer 返回 1、0 或 -1,该 integer 是正数、零或负数,仅使用按位 C 操作且没有“-” - How do you return 1, 0, or -1 based off a given integer that is positive, zero or negative using only bitwise C operations and no "-" 对于非零&#39;b&#39;,表达式“(a / b)* b + a%b - a”在C中总是为零? - How is the expression “(a/b)*b + a%b - a” always zero in C for non-zero 'b'? 为什么在C中将较大的double转换为long有时返回正数,而其他时候返回负数? - Why does casting a large double to a long sometimes return a positive and other times return a negative value in C?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM