Why does fmax (a, b) return a smaller (negative) zero and how is it a workaround?

#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 (incorrect) behavior is not documented in the fmax man page. Is there a reasonable explanation? And is there a short (short) workaround? Also, if both values ​​are -0.0, I would like to get -0.0 as max.

+4
source share
5 answers

The "problem" is that a == b. The sign does not matter, because the mantissa (the sign is delayed) is pure 0. I get 0x80000000vs0

, fmax , a < b b < a ( ), , .

gcc fmax(0.0,-0.0) 0.0, fmax(-0.0,0.0) -0.0.

, memcmp 0.

, , 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));
}

( , ):

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
+7

fmax cppreference:

, , +0, - -0, 0.

, , -0.0.

+3

. IEEE754 -0.0 +0.0.

( , .)

, . , .

C99, .

+2

fmax (a, b) ()

fmax() . +0.0 -0.0 . a b fmax(). , :

fmax , fmax(−0. 0, +0. 0) +0; . C11 # 361


?

signbit(), +0.0 -0.0. +/- 0.0

signbit , C1dr §7.12.3.6 3

, - - (NaN). "" , .

> < >= <= , - NaN.
a > b a <= b. .

OP zero +0.0 -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;
}

, , else, fmaxf() - @Jean-François Fabre. : 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);
}
+1

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 use float, it is best to put fin a suffix, otherwise they will be interpreted as double.

I will leave the fminfsmacro type as an exercise.

0
source

Source: https://habr.com/ru/post/1674142/


All Articles