フロートを最小の増分(またはそれに近い値)で変更する方法は?
-
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;
}
まったく同じことをする必要があり、次のコードを思いつきました:
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);
}