Cに符号なし浮動小数点数がないのはなぜですか?


131

質問は奇妙なようです。プログラマーは時々考えすぎます。読んでください...

CIでは整数signedunsigned整数を使用します。符号なし整数に符号付き整数を割り当てるなどのことを行うと、コンパイラーが警告するという事実が気に入っています。符号付き整数と符号なし整数などを比較すると、警告が表示されます。

私はこれらの警告が好きです。彼らは私が私のコードを正しく保つのを助けます。

フロートに同じ贅沢をしてみませんか?平方根が絶対に負の数を返すことは決してありません。負の浮動小数点値が意味をなさない他の場所もあります。符号なしフロートの完璧な候補。

ところで-私は、浮動小数点数から符号ビットを削除することで得られる精度の1つの余分なビットにあまり興味がありません。float彼らは今であるので、私はsにとても満足しています。浮動小数点を符号なしとしてマークし、整数で取得するのと同じ種類の警告を取得したいだけです。

符号なし浮動小数点数をサポートするプログラミング言語については知りません。

それらが存在しない理由は何ですか?


編集:

x87 FPUには、符号なし浮動小数点数を処理するための命令がないことを知っています。署名されたfloat命令を使用できます。誤用(たとえば、ゼロ未満になる)は、符号付き整数のオーバーフローが未定義であるのと同じように、未定義の動作と見なすことができます。


4
おもしろいですが、署名のタイプチェックが役に立った事例の例を投稿できますか?

litb、あなたのコメントは私に向けられましたか?もしそうなら、私はそれを取得しません

Iraimbilanjaええ:)ファブは引数の絶対値を返すため、負の数を返すことはできません
Johannes Schaub-litb

Right.iは、仮想のunsignedfloatがどのようにして正当性を助けることができるかを尋ねていませんでした。タイプセーフに関して

1
ポイントインレンジチェック用の符号なしマイクロ最適化があります:((unsigned)(p-min))<(max-min)これは1つのブランチしかありませんが、いつものように、プロファイルを作成してそれは本当に役に立ちます(私は主に386コアで使用したため、最近のCPUがどのように対応するのかわかりません)。
2009

回答:


114

C ++が符号なし浮動小数点数をサポートしていないのは、CPUが実行する同等のマシンコード操作がないためです。したがって、それをサポートすることは非常に非効率的です。

C ++がサポートしている場合は、符号なし浮動小数点数を使用していて、パフォーマンスが低下したことに気付かない場合があります。C ++でサポートされている場合は、すべての浮動小数点演算をチェックして、署名されているかどうかを確認する必要があります。何百万もの浮動小数点演算を行うプログラムの場合、これは受け入れられません。

したがって、問題は、ハードウェア実装者がそれをサポートしない理由です。そして、その答えは、最初に定義された符号なしフロート標準がなかったということです。言語は下位互換性を望んでいるため、追加された言語でもそれを利用できませんでした。浮動小数点の仕様を確認するには、IEEE標準の754 Floating-Pointを参照してください。

floatまたはdoubleをカプセル化し、負の数を渡そうとすると警告をスローするunsigned floatクラスを作成することで、unsigned浮動小数点型を回避できます。これは効率的ではありませんが、おそらくそれらを激しく使用していなければ、わずかなパフォーマンスの低下を気にする必要はありません。

符号なしフロートを使用することの有用性は間違いなくわかります。しかし、C / C ++は、安全性よりもすべての人に最適な効率を選択する傾向があります。


17
C / C ++は、言語を実装するために特定のマシンコード操作を必要としません。初期のC / C ++コンパイラは、386(FPUのないCPU)の浮動小数点コードを生成できました。コンパイラーは、FPU命令をエミュレートするライブラリー呼び出しを生成します。したがって、ufloatはCPUサポートなしで実行できます
Skizz

10
スキッ、それは正しいが、ブライアンはすでにこれに対処している-同等のマシンコードがないため、パフォーマンスは比較すると恐ろしいものになるだろう。
アンソニー

2
@ブライアンR.ボンディ:「CPUを実行するための同等のマシンコード操作がないため...」簡単に説明していただけますか?
Lazer

2
OPが符号なしfloatのサポートを必要とした理由は警告メッセージのためでした。そのため、実際にはコンパイラのコード生成フェーズとは何の関係もありません-事前に型チェックを行う方法とのみ関係があるため、マシンコードでのサポートは関係ありません。 (質問の最後に追加したように)通常の浮動小数点命令を実際の実行に使用できます。
ジョーF

2
これがパフォーマンスに影響を与える理由がわかりません。と同様にint、すべての符号関連の型チェックはコンパイル時に行われる可能性があります。OPは、特定の意味のない操作が実行されないようにするためのコンパイル時チェックを備えunsigned floatた通常の方法floatで実装されることを示唆しています。結果のマシンコードとパフォーマンスは、フロートが署名されているかどうかに関係なく、同じになる可能性があります。
xanderflood 2017

14

C / C ++では、符号付き整数と符号なし整数の間に大きな違いがあります。

value >> shift

符号付きの値は上位ビットを変更せずにそのままにし(符号拡張)、符号なしの値は上位ビットをクリアします。

符号なし浮動小数点数がない理由は、負の値がない場合、あらゆる種類の問題がすぐに発生するためです。このことを考慮:

float a = 2.0f, b = 10.0f, c;
c = a - b;

cにはどのような価値がありますか?-8。しかし、負の数がないシステムでは、それはどういう意味でしょうか。FLOAT_MAX-8多分?実際には、FLOAT_MAX-8は精度の影響によりFLOAT_MAXであるため機能しません。それがより複雑な式の一部であった場合はどうなるでしょうか。

float a = 2.0f, b = 10.0f, c = 20.0f, d = 3.14159f, e;
e = (a - b) / d + c;

2の補数システムの性質上、これは整数の問題ではありません。

また、標準の数学関数も考慮してください。sin、cos、tanは入力値の半分に対してのみ機能し、値の対数<1を見つけることができず、2次方程式を解くことができませんでした:x =(-b +/- root( bb-4.ac))/ 2.aなど。実際、これらの関数は多項式近似として実装される傾向があり、どこかで負の値を使用するため、複雑な関数ではおそらく機能しません。

したがって、符号なし浮動小数点数はほとんど役に立ちません。

しかし、これは、範囲が浮動小数点値をチェックするクラスが役に立たないと言っているわけではありません。たとえば、RGB計算のように、値を特定の範囲にクランプしたい場合があります。


@Skizz:表現に問題がある場合、誰かがと同じくらい効率的なフロートを格納するメソッドを考案できれば2's complement、符号なしフロートがあっても問題はありませんか?
Lazer、2010

3
value >> shift for signed values leave the top bit unchanged (sign extend) よろしいですか?これは、少なくとも負の符号付き値の場合、実装定義の動作であると思いました。
ダン、

@Dan:最近の標準を見ただけで、実際には実装で定義されていることがわかります。これは、符号拡張命令で右シフトしないCPUがある場合に備えていると思います。
Skizz


1
浮動小数点は伝統的に、ラッピングする代わりに(-/ + Infに)飽和します。符号なし減算オーバーフローが0.0、またはInfまたはNaN に飽和することが予想される場合があります。または、質問の編集で提案されたOPのように、Undefined Behaviourになるだけです。Re:関数のトリガー:などの符号なし入力バージョンを定義しないでくださいsin。また、戻り値を符号付きとして扱うようにしてください。問題は floatをunsigned floatに置き換えることではなくunsigned float、新しい型として追加することです。
Peter Cordes

9

(余談ですが、Perl 6では、

subset Nonnegative::Float of Float where { $_ >= 0 };

Nonnegative::Float他のタイプと同じように使用できます。)

符号なし浮動小数点演算はハードウェアでサポートされていないため、Cでは提供されていません。Cは、主に「ポータブルアセンブリ」、つまり特定のプラットフォームに縛られることなく、金属にできるだけ近くなるように設計されています。

[編集]

Cはアセンブリのようなものです。見たとおりのものが得られます。「このフロートが負でないことを確認します」という暗黙の暗示は、その設計哲学に反します。本当に必要な場合は、追加assert(x >= 0)または同様のことができますが、明示的に行う必要があります。


svn.perl.org/parrot/trunk/languages/perl6/docs/STATUSは「はい」と言いますが、of ...解析しません。
ephemient 2009

8

私は、signed intが提供できるよりも大きな値のマージンが必要なため、unsigned intが作成されたと思います。

フロートのマージンははるかに大きいため、符号なしフロートが「物理的に」必要になることはありませんでした。そして、あなたがあなたの質問であなた自身を指摘しているように、追加の1ビット精度は何を殺すものではありません。

編集:ブライアンR.ボンディの回答を 読んだ後、私は私の回答を変更する必要があります。しかし、私はこれが上で述べた理由に基づいた設計上の決定であるという私の信念を維持します;-)


2
また、整数の加算と減算は、符号付きまたは符号なしと同じです-浮動小数点です。そのような機能の比較的低い限界効用を考えると、署名されたフロートと署名されていないフロートの両方をサポートするために誰が追加の作業を行うでしょうか?
ephemient 2009

7

Trebは正しい方向に進んでいると思います。整数の場合は、符号なしの対応する型を持っていることがより重要です。これらは、ビットシフトで使用され、ビットマップで使用されるものです。サインビットが邪魔になります。たとえば、負の値を右シフトすると、結果の値はC ++で定義された実装になります。符号なし整数でそれを実行するか、そのようなものをオーバーフローすると、そのようなビットがないため、セマンティクスは完全に定義されています。

したがって、少なくとも整数の場合、別個の符号なしタイプの必要性は、警告を出すだけの場合よりも強力です。上記のすべてのポイントをフロートで考慮する必要はありません。ですから、ハードウェアのサポートは実際には必要なく、Cはその時点ではすでにサポートしていません。


5

平方根は間違いなく負の数を返しません。負の浮動小数点値が意味をなさない他の場所もあります。符号なしフロートの完璧な候補。

C99は複素数とsqrtの型ジェネリック形式をサポートしているため、sqrt( 1.0 * I)負になります。


コメンテーターsqrtは、関数ではなく型ジェネリックマクロを参照していて、複素数を実際のコンポーネントに切り捨てることによってスカラー浮動小数点値を返すという点で、上記のわずかな光沢を強調しました。

#include <complex.h>
#include <tgmath.h>

int main () 
{
    complex double a = 1.0 + 1.0 * I;

    double f = sqrt(a);

    return 0;
}

また、任意の複素数のsqrtの実数部が正またはゼロであり、sqrt(1.0 * I)がsqrt(0.5)+ sqrt(0.5)* Iであるため、-1.0ではなく、頭脳が含まれています。


はい。ただし、複素数を扱う場合は、別の名前で関数を呼び出します。また、戻り値の型も異なります。良い点だけど!
Nils Pipenbrinck

4
sqrt(i)の結果は複素数です。そして、複素数を発注されていないので、あなたは複素数はnegativ(すなわち<0)であると言うことはできません
quinmars

1
quinmars、それがcsqrtではないことを確認してください?または、Cの代わりに数学について話しますか?私はとにかくそれが良い点だと同意します:)
Johannes Schaub-litb

確かに、私は数学について話していました。私はcの複素数を扱ったことがありません。
キンマーズ2009

1
「平方根は間違いなく負の数を返しません。」-> sqrt(-0.0)しばしば生成し-0.0ます。もちろん、-0.0は負の値ではありません。
chux-モニカを2015年

4

IEEE浮動小数点の仕様が署名されていることと、ほとんどのプログラミング言語がそれらを使用していることによると思います。

IEEE-754浮動小数点数に関するウィキペディアの記事

編集:また、他の人が指摘しているように、ほとんどのハードウェアは負でない浮動小数点数をサポートしていないため、ハードウェアサポートがあるため、通常の種類の浮動小数点数の方が効率的です。


Cは、IEEE-754標準が登場するずっと前に導入されました
phuclv

@phuclvどちらも一般的な浮動小数点ハードウェアではありませんでした。「数年」後に標準Cに採用されました。それについてインターネット上に浮かんでいるドキュメントがおそらくあるでしょう。(また、ウィキペディアの記事ではC99について言及しています)。
トビアスワーレ

どういう意味かわかりません。そこにあなたの答えには、「ハードウェアは」ません、およびCにおける浮動小数点型は、IEEE-754規格に依存することはできませんので、これらのタイプは、かなり後にCに導入した場合を除き、IEEE-754は、Cの後に生まれた
phuclvを

@phuclv Cはポータブルアセンブリとしても知られているため、ハードウェアに非常に近い可能性があります。言語は長年にわたって機能を獲得していますが、(私の時間の前に)floatがCで実装されたとしても、おそらくソフトウェアベースの操作であり、非常に高価でした。この質問に答えたとき、私は今説明していることよりも今説明しようとしていることをよく理解していました。そして、あなたが受け入れられた答えを見れば、IEE754標準に言及した理由を理解するかもしれません。私が理解していないことは、あなたが受け入れられていない10年前の答えを選んだということですか?
TobiasWärre19年

3

主な理由は、符号なしfloatは、符号なしintに比べて実際に使用が制限されるためです。ハードウェアがそれをサポートしていないからだという意見は買いません。古いプロセッサには浮動小数点機能がまったくなく、すべてソフトウェアでエミュレートされていました。署名されていないフロートが有用である場合、それらは最初にソフトウェアで実装され、ハードウェアもそれに準ずるでしょう。


4
Cの最初のプラットフォームであるPDP-7には、オプションのハードウェア浮動小数点ユニットがありました。Cの次のプラットフォームであるPDP-11は、ハードウェアに32ビットフロートを備えていました。80x86は1世代後期になり、いくつかのテクノロジーは1世代遅れていました。
ephemient 2009

3

Cの符号なし整数型は、抽象代数環の規則に従うような方法で定義されます。たとえば、XとYの値の場合、XYをYに追加するとXが生成されます。符号なし整数型は、他の数値型との間の変換を含まないすべてのケースでこれらのルールに従うことが保証されます[または異なるサイズの符号なし型] 、そしてその保証は、そのようなタイプの最も重要な機能の1つです。場合によっては、符号なしの型のみが提供できる追加の保証と引き換えに、負の数を表す機能を放棄することは価値があります。浮動小数点型は、符号付きかどうかにかかわらず、代数環のすべての規則に従うことができません[たとえば、X + YYがXと等しいことを保証することはできません]。実際、IEEEはそうではありません また、[特定の値が自分自身と等しくないことを要求することによって]等価クラスの規則を遵守することもできます。「符号なし」の浮動小数点型は、通常の浮動小数点型では実現できなかった公理を遵守できないと思います。そのため、どのような利点があるのか​​わかりません。


1

IHMOこれは、ハードウェアまたはソフトウェアのいずれかで符号付きと符号なしの両方の浮動小数点型をサポートするのは面倒すぎるためです。

整数型のために我々は利用することができ、同じのための論理ユニットの両方の符号付きと符号なし整数演算ので、2の補数の素敵なプロパティを使用して、ほとんどの状況での結果は、これらの例では同じである非拡大MUL、追加、サブのための最もビット演算。署名されたバージョンと署名されていないバージョンを区別する操作では、ロジックの大部分を共有できます。例えば

  • 算術および論理シフトでは、上位ビットのフィラーをわずかに変更するだけで済みます。
  • 拡大乗算では、主要部分に同じハードウェアを使用し、その後、結果を調整して符号を変更するためのいくつかの個別のロジックを使用できます。実際の乗算器では使用されていませんが、実行することは可能です
  • 符号付き比較は符号なし比較に変換でき、その逆は、上位ビットを切り替えるか、を追加することでINT_MIN簡単にできます。また、理論的には可能ですが、ハードウェアでは使用されない可能性がありますが、1つのタイプの比較のみをサポートするシステム(8080や8051など)で役立ちます。

1の補数を使用するシステムでも、キャリービットが最下位ビットにラップされるだけなので、ロジックを少し変更するだけで済みます。符号の大きさのシステムについては不明ですが、内部では1の補数使用しているため、同じことが当てはまります。

残念ながら、浮動小数点型にはそれほど贅沢ではありません。署名ビットを解放するだけで、署名されていないバージョンになります。しかし、そのビットを何に使用すればよいでしょうか。

  • 指数に追加して範囲を広げます
  • 仮数に追加して精度を高めます。通常、範囲よりも高い精度が必要なため、これは多くの場合より便利です。

ただし、どちらの選択でも、より広い値の範囲に対応するには、より大きな加算器が必要です。これにより、ロジックの複雑さが増しますが、加算器の最上位ビットはほとんどの場合未使用のままです。乗算、除算、その他の複雑な演算にはさらに多くの回路が必要になります

ソフトウェア浮動小数点を使用するシステムでは、メモリが非常に高価だったときに予期されなかった関数ごとに2つのバージョンが必要でした。

ただし、浮動小数点ハードウェアはCが発明されるずっと以前から存在していたので、Cを選択したのは、前述の理由によりハードウェアサポートが不足していたためだと思います。

とはいえ、主に画像処理を目的とした、Khronosグループの10ビットおよび11ビットの浮動小数点型など、いくつかの特殊な符号なし浮動小数点形式が存在します。


0

Cコンパイラのターゲットとなる基本的なプロセッサには、符号なし浮動小数点数を処理する適切な方法がないためだと思います。


基盤となるプロセッサは、符号付き浮動小数点数を処理する良い方法を持っていますか?Cは、浮動小数点補助プロセッサが特異的でほとんど普遍的ではなかったときに人気が高まりました。
David Thornley、

1
過去のタイムラインをすべて知っているわけではありませんが、ご指摘のとおり、まれですが、署名付きフロートのハードウェアサポートが出現しました。言語設計者は、サポートを組み込むことができますが、コンパイラバックエンドは、対象となるアーキテクチャに応じてさまざまなレベルのサポートを提供しました。
Brian Ensink 09

0

良い質問。

あなたが言うように、それがコンパイル時の警告のみであり、それらの動作に変化がない場合、それ以外の場合、基盤となるハードウェアは影響を受けないため、C ++ /コンパイラの変更のみになります。

私は以前に同じことを疑問視しましたが、問題は次のとおりです。それはあまり役に立ちません。コンパイラはせいぜい静的割り当てを見つけることができます。

unsigned float uf { 0 };
uf = -1f;

または最小限に長く

unsigned float uf { 0 };
float f { 2 };
uf -= f;

しかし、それで終わりです。符号なし整数型を使用すると、ラップアラウンドも定義されます。つまり、モジュラー演算のように動作します。

unsigned char uc { 0 };
uc -= 1;

この後、「uc」は255の値を保持します。

さて、コンパイラは、符号なし浮動小数点型が与えられた場合、同じシナリオで何をしますか?コンパイル時に値がわからない場合は、最初に計算を実行してから符号チェックを行うコードを生成する必要があります。しかし、そのような計算の結果が「-5.5」となるとどうなるでしょうか-符号なしで宣言されたフロートにどの値を格納する必要がありますか?整数型のようにモジュラー算術を試すこともできますが、それ自体に問題があります。最大の値は間違いなく無限大です。...機能しません。「無限大-1」は使用できません。それが保持できる最大の明確な値を求めても、その精度にぶつかると実際には機能しません。「NaN」が候補になります。

最後に、モジュロが明確に定義されているため、これは固定小数点数の問題ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.