フロートを最小の増分(またはそれに近い値)で変更する方法は?

StackOverflow https://stackoverflow.com/questions/155378

  •  03-07-2019
  •  | 
  •  

質問

double の値 f があり、それを非常にわずかに大きく(または小さく)して、できる限り近い新しい値を取得する方法を希望しています元の値よりも厳密に元の値よりも大きい(または小さい)

最後のビットに近づける必要はありません—変更は、元の値に戻らずに異なる値を生成することが保証されることがより重要です。

役に立ちましたか?

解決

math.hファイルを確認します。運がよければ nextafter および nextafterf 関数が定義されています。これらは、ポータブルでプラットフォームに依存しない方法で必要なことを正確に実行し、C99標準の一部です。

別の方法(フォールバックソリューションかもしれません)は、フロートを仮数部と指数部に分解することです。インクリメントは簡単です:仮数に1を追加するだけです。オーバーフローが発生した場合は、指数をインクリメントしてこれを処理する必要があります。デクリメントも同様に機能します。

編集:コメントで指摘したように、バイナリ表現のフロートをインクリメントするだけで十分です。仮数オーバーフローにより指数が増加しますが、これがまさにそのとおりです。

これは一言で言えばnextafterと同じことです。

これは完全にポータブルではありません。エンディアンと、すべてのマシンがIEEEフロートを持っているわけではないという事実に対処する必要があります(最後の理由はよりアカデミックです)。

また、NANと無限を処理するのは少し難しい場合があります。定義では数字ではなく、単純にインクリメントすることはできません。

他のヒント

u64 &x = *(u64*)(&f);
x++;

はい、真剣に。

編集:誰かが指摘したように、これは-ve数、Inf、Nan、またはオーバーフローを適切に処理しません。上記のより安全なバージョンは

です
u64 &x = *(u64*)(&f);
if( ((x>>52) & 2047) != 2047 )    //if exponent is all 1's then f is a nan or inf.
{
    x += f>0 ? 1 : -1;
}

絶対的に言えば、浮動小数点値に追加して新しい個別の値を作成できる最小量は、値の現在の大きさに依存します。タイプのマシンイプシロンに現在の指数を掛けたものになります。

浮動小数点の表現については、 IEEE仕様をご覧ください。最も簡単な方法は、値を整数型として再解釈し、1を追加してから、符号を反転したり、符号ビットと指数ビットを調べてNaNを生成したりしていないことを確認します。

あるいは、 frexp を使用して現在の仮数を取得できます。および指数、したがって追加する値を計算します。

まったく同じことをする必要があり、次のコードを思いつきました:

double DoubleIncrement(double value)
{
  int exponent;
  double mantissa = frexp(value, &exponent);
  if(mantissa == 0)
    return DBL_MIN;

  mantissa += DBL_EPSILON/2.0f;
  value = ldexp(mantissa, exponent);
  return value;
}

価値がある場合、標準++の増分が機能しなくなる値は9,007,199,254,740,992です。

これは希望どおりではないかもしれませんが、 numeric_limits が使用されています。特に、メンバーmin()およびepsilon()。

mydouble + numeric_limits :: epsilon()のようなものが、mydoubleがすでにイプシロンに近い場合を除き、あなたが望むことをするとは思わない。もしそうなら、あなたは幸運です。

このコードはしばらく前に発見されました。おそらく、それをプッシュアップできる最小値を判断するのに役立ち、その値でインクリメントするだけです。残念ながら、このコードのリファレンスを思い出せません:

#include <stdio.h>

int main()
{
    /* two numbers to work with */
    double number1, number2;    // result of calculation
    double result;
    int counter;        // loop counter and accuracy check

    number1 = 1.0;
    number2 = 1.0;
    counter = 0;

    while (number1 + number2 != number1) {
        ++counter;
        number2 = number2 / 10;
    }
    printf("%2d digits accuracy in calculations\n", counter);

    number2 = 1.0;
    counter = 0;

    while (1) {
        result = number1 + number2;
        if (result == number1)
            break;
        ++counter;
        number2 = number2 / 10.0;
    }

    printf("%2d digits accuracy in storage\n", counter );

    return (0);
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top