1.0に最も近いdoubleは何ですか、それは1.0ではありませんか?


88

プログラムで1.0に最も近いdoubleを取得する方法はありますが、実際には1.0ではありませんか?

これを行うハックな方法の1つは、doubleを同じサイズの整数にmemcpyしてから、1を減算することです。IEEE754浮動小数点形式が機能する方法では、これにより、小数部をすべてゼロ(1.000000000000)からすべて1(1.111111111111)に変更しながら、指数が1つ減少します。ただし、整数がリトルエンディアンで格納され、浮動小数点がビッグエンディアンで格納されるマシンが存在するため、常に機能するとは限りません。


4
+1が-1と同じ距離(1.0から)であるとは仮定できません。基数10と基数2の浮動小数点表現のインターリーブは、ギャップが不均一であることを意味します。
Richard Critten

2
@リチャード:そうだね。1つのULPを減算しても「nextbefore」という値が得られる可能性はほとんどありません。これは、指数も調整する必要があるためだと思います。nextafter()彼が望んでいることを達成する唯一の適切な方法です。
Rudy Velthuis、2016

1
FYIこのブログ(ない鉱山)の読み取りを持っている:exploringbinary.com/...
リチャードCritten

1
@RudyVelthuis:すべてのIEEE754バイナリ浮動小数点形式で機能します。
Edgar Bonet、2016

1
では、「すべてのIEEE754浮動小数点形式で機能するのは何ですか」と教えてください。仮数をデクリメントすると「firstbefore()」の値が得られることは、特に1.0の場合は2の累乗である仮数があるため、単純に真実ではありません。つまり、1.0000...バイナリはデクリメントさ0.111111....れて正規化されるため、左にシフトする1.11111...必要があります。これにより、指数をデクリメントする必要があります。そして、あなたは1.0から2 ulp離れています。したがって、いいえ、整数値から1を減算しても、ここで求められていることはわかりません。
Rudy Velthuis 16

回答:


22

CおよびC ++では、次の値が1.0に最も近い値を示します。

#include <limits.h>

double closest_to_1 = 1.0 - DBL_EPSILON/FLT_RADIX;

ただし、C ++の以降のバージョンでlimits.hは、が非推奨になっていることに注意してくださいclimits。ただし、とにかくC ++固有のコードを使用している場合は、

#include <limits>

typedef std::numeric_limits<double> lim_dbl;
double closest_to_1 = 1.0 - lim_dbl::epsilon()/lim_dbl::radix;

Jarod42が彼の回答に書いているように、C99またはC ++ 11以降は次のように使用することもできますnextafter

#include <math.h>

double closest_to_1 = nextafter(1.0, 0.0);

もちろんC ++ではcmathstd::nextafter代わりにインクルードして使用することができます(以降のC ++バージョンの場合はそうする必要があります)。


143

C ++ 11以降、nextafter次の表現可能な値を特定の方向に取得するために使用できます。

std::nextafter(1., 0.); // 0.99999999999999989
std::nextafter(1., 2.); // 1.0000000000000002

デモ


11
これは、doubleを次の表現可能な整数にインクリメントするための良い方法でもありますstd::ceil(std::nextafter(1., std::numeric_limits<double>::max()))
ヨハネスシャウブ-litb

43
P:「これはSTDLIBに実装されているか」であることを行って次の質問者
軌道上での明度レース

17
@LightnessRacesinOrbitのコメントを読んだ後、気になった。これがglibcの実装方法nextafterあり、他の誰かがそれがどのように行われたかを見たい場合に備えてmuslが実装する方法です。基本的には、生のビットいじり。
Cornstalks

2
@Cornstalks:私がそれが少しいじくり回していることに驚いていない、他の唯一のオプションはCPUサポートを持つことです。
Matthieu M.16年

5
IMO、ビットをいじるのが正しくそれを行う唯一の方法です。ゆっくりとアプローチしようとして、多くのテストを実行できますが、非常に遅くなる可能性があります。
Rudy Velthuis 16

25

Cでは、これを使用できます。

#include <float.h>
...
double value = 1.0+DBL_EPSILON;

DBL_EPSILON 1と、1より大きい、表現可能な最小値との差です。

実際の値を確認するには、それを数桁に印刷する必要があります。

私のプラットフォームでprintf("%.16lf",1.0+DBL_EPSILON)は、を与え1.0000000000000002ます。


10
したがって、それ1.1'000'000 デモ
ません

7
@ Jarod42:その通りですが、OPは特にについて質問し1.0ます。ところで、これは1より大きい最も近い値を提供し、1に絶対的に最も近い値(1より小さい可能性があります)ではありません。これは部分的な答えであることに同意しますが、それでも貢献できると思いました。
barak manos

@LưuVĩnhPhúc:答えの制限について正確に説明し、反対方向に最も近い。
Jarod42

7
これは与えていない、最も近い(ベース2を仮定)として、1.0に二重の権利をダブルをする前に 1.0は半分だけ、遠くダブル権利としてある 1.0(あなたが求める一つです)。
celtschk

@celtschk:そうですね、上のコメントで説明しました。
barak manos

4

C ++では、これも使用できます

1 + std::numeric_limits<double>::epsilon()

1
同様バラクマノスと答え、これが1以外の値では動作しません
zwol

2
@zwolは、典型的な2進浮動小数点実装では、1から2イプシロンまでの任意の値で機能します。しかし、はい、あなたはそれが1にのみ適用されることが保証されているのは正しいです
。– Random832

7
技術的には、1に最も近い数は1の直後ではなく、1の直前の数であるため、1に対しては機能しません。doubleの0.5と1の間の精度は、1と2の間の精度の2倍であるため、1の直前の数値は1に近くなります
。– HelloGoodbye
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.