145

私は知っています、質問は奇妙に思えます。プログラマーは時々考えすぎます。読んでください...

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

私はこれらの警告が好きです。コードを正しく保つのに役立ちます。

山車にも同じ贅沢をしてみませんか?平方根が負の数を返すことは絶対にありません。負の float 値が意味をなさない場所は他にもあります。unsigned float の完全な候補です。

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

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

なぜそれらが存在しないのか考えていますか?


編集:

x87 FPU には符号なし浮動小数点数を処理する命令がないことはわかっています。署名されたフロート命令を使用しましょう。符号付き整数のオーバーフローが定義されていないのと同じように、誤用 (例: ゼロを下回る) は未定義の動作と見なされる可能性があります。

4

13 に答える 13

123

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

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

したがって、問題は、ハードウェアの実装者がそれをサポートしない理由です。そして、それに対する答えは、元々定義されていた unsigned float 規格がなかったということだと思います。言語は下位互換性を好むため、追加された言語であってもそれを利用できませんでした。浮動小数点の仕様を確認するには、IEEE 標準 754 Floating-Pointを確認する必要があります。

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

unsigned float を持つことの有用性は間違いなくわかります。しかし、C/C++ は、安全性よりも、誰にとっても最適な効率性を選択する傾向があります。

于 2009-02-04T16:16:34.803 に答える
20

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

value >> shift

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

unsigned float がない理由は、負の値がないとあらゆる種類の問題にすぐに遭遇するためです。このことを考慮:

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 未満の値の対数を見つけることができず、二次方程式を解くことができませんでした: x = (-b +/- root ( bb - 4.ac)) / 2.a など。実際、これらはどこかで負の値を使用する多項式近似として実装される傾向があるため、複雑な関数ではおそらく機能しません。

したがって、unsigned float はほとんど役に立ちません。

しかし、だからといって float 値の範囲をチェックするクラスが役に立たないというわけではありません。たとえば、RGB 計算など、値を特定の範囲に固定したい場合があります。

于 2009-02-05T01:14:36.917 に答える
10

(余談ですが、Perl6では次のように書くことができます

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

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

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

[編集]

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

于 2009-02-04T16:24:12.463 に答える
9

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

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

編集: Brian R. Bondy の回答を読んだ後、回答を修正する必要があります: 彼は、基になる CPU に符号なし浮動小数点演算がなかったことは間違いありません。ただし、これは上記の理由に基づいた設計上の決定であると私は信じています ;-)

于 2009-02-04T16:18:51.897 に答える
7

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

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

于 2009-02-04T16:27:06.747 に答える
6

平方根が負の数を返すことは絶対にありません。負の float 値が意味をなさない場所は他にもあります。unsigned float の完全な候補です。

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 であるため、脳のおならも含まれています。

于 2009-02-04T16:31:59.687 に答える
5

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

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

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

于 2009-02-04T16:12:36.683 に答える
3

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

于 2014-06-01T21:20:47.980 に答える
3

主な理由は、unsigned int と比較して、unsigned float の用途が非常に限られているためだと思います。ハードウェアがサポートしていないからという議論には同意しません。古いプロセッサには浮動小数点機能がまったくなく、すべてソフトウェアでエミュレートされていました。符号なし浮動小数点数が有用である場合、それらは最初にソフトウェアで実装され、ハードウェアがそれに続いたでしょう。

于 2009-02-04T17:51:20.493 に答える
1

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

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

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

1 の補数を使用するシステムでは、キャリー ビットが最下位ビットにラップされるだけなので、ロジックを少し変更する必要があります。サインマグニチュードシステムについてはわかりませんが、内部で1の補数を使用しているように見えるので、同じことが当てはまります

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

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

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

ソフトウェア浮動小数点を使用するシステムでは、関数ごとに 2 つのバージョンが必要です。これは、メモリが非常に高価だった時代には予期されていなかったことです。または、符号付き関数と符号なし関数の一部を共有するための「トリッキーな」方法を見つける必要があります。

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

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

于 2019-02-27T17:03:32.953 に答える
0

これは、C コンパイラが対象としている基礎となるプロセッサが、符号なし浮動小数点数を処理する適切な方法を持っていないためだと思われます。

于 2009-02-04T16:13:18.460 に答える
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 になります。

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

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

于 2020-04-23T07:10:18.250 に答える