2017-04-17 4 views
0

Pour deux nombres à virgule flottante (IEEE simple ou double précision), je voudrais trouver le nombre qui se trouve à mi-chemin entre eux, mais pas dans le sens de (x + y)/2 en ce qui concerne les nombres réellement représentables.calculer le point milieu en virgule flottante

si x et y sont positifs, les travaux suivants

float ieeeMidpoint(float x, float y) 
{ 
    assert(x >= 0 && y >= 0); 
    int xi = *(int*)&x; 
    int yi = *(int*)&y; 
    int zi = (xi+yi)/2; 
    return *(float*)&zi; 
} 

La raison pour laquelle cela fonctionne est que les nombres à virgule flottante (y compris positifs ieee et l'infini) aux anormaux gardent leur ordre lorsque vous faites un casting réinterprétant. (Ce n'est pas vrai pour le format étendu 80 bits, mais je n'en ai pas besoin de toute façon).

Maintenant je cherche une manière élégante de faire la même chose qui inclut le cas quand un ou les deux nombres sont négatifs. Bien sûr, il est facile de faire avec un tas de si, mais je me demandais s'il y avait une belle magie des bits, préférablement sans ramification.

+0

Quelle est la définition de "point milieu" lorsque les opérandes ont un signe opposé? Par exemple, quel est le résultat souhaité de 'ieeeMidPoint (-1,4)'. Ce n'est pas clair. Notez que les tentatives de réinterpréter le stockage dans différents types de la manière indiquée (par la conversion de pointeur) invoquent un comportement indéfini dans C et C++. Cela devient généralement apparent si vous compilez avec des niveaux d'optimisation plus élevés. Suggestion: Si vous utilisez C, utilisez une union ('volatile')', si vous utilisez C++, utilisez 'mempcy()' pour réinterpréter les données à virgule flottante en entiers et vice-versa. – njuffa

+0

$ z $ est le point milieu entre $ x $ et $ y $ s'il y a le même nombre de nombres représentables dans $ [x, z] $ que dans $ [z, y] $. L'exposant et la mantisse ont tous les deux des bits limités, donc ces comptes seront toujours finis. – Simon

Répondre

0

Je l'ai inventé moi-même. l'ordre du nombre négatif est inversé lors de la réinterprétation de la distribution, c'est donc la seule chose à corriger. Cette version est plus longue que ce que j'espérais, mais il ne s'agit que d'un peu de mélange, donc ça devrait être rapide.

float ieeeMidpoint(float x, float y) 
{ 
    // check for NaN's (Note that subnormals and infinity work fine) 
    assert(x ==x && y == y); 

    // re-interpreting cast 
    int xi = *(int*)&x; 
    int yi = *(int*)&y; 

    // reverse negative numbers 
    // (would look cleaner with an 'if', but I like not branching) 
    xi = xi^((xi >> 31) & 0x3FFFFFFF); 
    yi = yi^((yi >> 31) & 0x3FFFFFFF); 

    // compute average of xi,yi (overflow-safe) 
    int zi = (xi >> 1) + (yi >> 1) + (xi & 1); 

    // reverse negative numbers back 
    zi = zi^((zi >> 31) & 0x3FFFFFFF); 

    // re-interpreting back to float 
    return *(float*)&zi; 
}