49

私はdouble値を持っており、元の値fにできるだけ近いが元の値よりも厳密に大きい (または小さい) 新しい値を取得するために、値をわずかに大きく (または小さく) 微調整する方法が必要です。

最後のビットに近づく必要はありません。どのような変更を行っても、異なる値が生成され、元の値に丸められないことが保証されることがより重要です。

4

7 に答える 7

65

math.h ファイルを確認してください。運が良ければ、 関数nextafternextafterf関数が定義されています。それらは、ポータブルでプラットフォームに依存しない方法で、まさにあなたが望むことを行い、C99 標準の一部です。

それを行う別の方法(フォールバックソリューションになる可能性があります)は、浮動小数点数を仮数部と指数部に分解することです。増分は簡単です。仮数に 1 を追加するだけです。オーバーフローが発生した場合は、指数をインクリメントしてこれを処理する必要があります。デクリメントも同じように機能します。

編集:コメントで指摘されているように、バイナリ表現でフロートをインクリメントするだけで十分です。仮数オーバーフローは指数をインクリメントします。これがまさに私たちが望んでいることです。

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

ただし、これは完全に移植可能ではありません。エンディアンと、すべてのマシンが IEEE float を持っているわけではないという事実に対処する必要があります (わかりました - 最後の理由はよりアカデミックです)。

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

于 2008-09-30T22:34:57.850 に答える
23
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;
}
于 2008-09-30T22:38:05.487 に答える
6

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

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

または、frexpを使用して現在の仮数と指数を取得し、加算する値を計算することもできます。

于 2008-09-30T22:38:36.077 に答える
2

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

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;
}
于 2008-09-30T22:47:22.453 に答える
0

これはまさにあなたが望むものではないかもしれませんが、numeric_limitsが使用されていることに気付くかもしれません。特に、メンバー min() と epsilon() です。

mydouble + numeric_limits::epsilon() のようなものが、mydouble がすでにイプシロンに近づいていない限り、あなたが望むことをするとは思いません。もしそうなら、あなたは幸運です。

于 2008-10-01T02:36:45.520 に答える
0

参考までに、標準の ++ インクリメントが機能しなくなる値は 9,007,199,254,740,992 です。

于 2008-09-30T23:01:20.437 に答える
-2

しばらく前にこのコードを見つけました。おそらく、それまでに押し上げることができる最小値を決定し、その値だけインクリメントするのに役立つでしょう。残念ながら、このコードのリファレンスを思い出せません:

#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);
}
于 2008-09-30T22:38:40.913 に答える