20

たとえば、2 つの int を除算して float を返したい場合、迷信的に次のように記述します。

int a = 2, b = 3;
float c = (float)a / (float)b;

aandを float にキャストしないとb、整数除算が行われて int が返されます。

同様に、符号付き 8 ビット数を符号なし 8 ビット数で乗算する場合は、オーバーフローを恐れて乗算する前に符号付き 16 ビット数にキャストします。

u8 a = 255;
s8 b = -127;
s16 = (s16)a * (s16)b;

まったくキャストしない場合、または変数の 1 つだけをキャストする場合、コンパイラはこれらの状況でどのように正確に動作しますか? すべての変数を明示的にキャストする必要がありますか、それとも左側の変数または右側の変数だけですか?

4

9 に答える 9

28

質問 1: フロート除算

int a = 2, b = 3;
float c = static_cast<float>(a) / b;  // need to convert 1 operand to a float

質問 2: コンパイラのしくみ

覚えておくべき 5 つの経験則:

  • 算術演算は、常に同じ型の値に対して実行されます。
  • 結果の型はオペランドと同じです (昇格後)
  • 算術演算が実行される最小の型は int です。
  • ANSCI C (および C++) は、整数昇格を保持する値を使用します。
  • 各操作は個別に行われます。

ANSI C の規則は次のとおりです。
これらの規則のほとんどは C++ にも適用されますが、すべての型が (まだ) 正式にサポートされているわけではありません。

  • いずれかのオペランドがlong doubleの場合、もう一方は long doubleに変換されます。
  • いずれかのオペランドがdoubleの場合、もう一方はdoubleに変換されます。
  • いずれかのオペランドがfloatの場合、もう一方はfloatに変換されます。
  • いずれかのオペランドがunsigned long longの場合、もう一方はunsigned long longに変換されます。
  • いずれかのオペランドがlong longの場合、もう一方はlong longに変換されます。
  • いずれかのオペランドがunsigned longの場合、もう一方はunsigned longに変換されます。
  • いずれかのオペランドがlongの場合、もう一方はlongに変換されます。
  • いずれかのオペランドがunsigned intの場合、もう一方はunsigned intに変換されます。
  • それ以外の場合、両方のオペランドがintに変換されます。

オーバーフロー

オーバーフローは常に問題です。ノート。結果の型は入力オペランドと同じであるため、すべての操作がオーバーフローする可能性があるため、心配する必要があります (ただし、言語はこれをキャッチする明示的な方法を提供していません)。

補足として:
符号なし除算はオーバーフローできませんが、符号付き除算はできます。

std::numeric_limits<int>::max() / -1  // No Overflow
std::numeric_limits<int>::min() / -1  // Will Overflow
于 2008-10-29T06:42:47.827 に答える
13

一般に、オペランドの型が異なる場合、コンパイラはすべてを最大または最も正確な型に昇格させます。

1 つの数値が... で、もう 1 つの数値が...
------------------- ------------------------------ ------------ -------------------
char int int
署名済み 署名なし 署名なし
char または int float float
フロート ダブル ダブル

例:

char + int ==> int
signed int + unsigned char ==> unsigned int
float + int ==> float

ただし、昇格は中間計算ごとに必要な場合にのみ発生することに注意してください。

4.0 + 5/3 = 4.0 + 1 = 5.0

これは、整数除算が最初に実行され、次に結果が加算のために float に昇格されるためです。

于 2008-10-29T04:25:27.027 に答える
5

それらの1つだけをキャストできます。どちらでも構いません。

型が一致しない場合は常に、「小さい」型が自動的に「大きい」型に昇格され、浮動小数点は整数型よりも「大きい」ものになります。

于 2008-10-29T04:02:00.477 に答える
2

整数の除算: オペランドのいずれかをキャストします。両方をキャストする必要はありません。両方のオペランドが整数の場合、除算演算は整数除算になり、それ以外の場合は浮動小数点除算になります。

オーバーフローの質問については、コンパイラが暗黙的にキャストするため、明示的にキャストする必要はありません。

#include <iostream>
#include <limits>

using namespace std;
int main()
{
    signed int a = numeric_limits<signed int>::max();
    unsigned int b = a + 1; // implicit cast, no overflow here
    cout << a << ' ' <<  b << endl;
    return 0;
}
于 2008-10-29T04:01:29.230 に答える
1

浮動小数点除算の場合、一方の変数が浮動小数点データ型 (float または double) である限り、もう一方の変数を浮動小数点型に拡張し、浮動小数点除算を行う必要があります。したがって、両方を float にキャストする必要はありません。

そうは言っても、とにかく、私は常に両方をフロートにキャストします。

于 2008-10-29T04:02:52.673 に答える
1

2 つの変数の 1 つだけをキャストしている限り、コンパイラは適切に動作すると思います (少なくとも、私が知っているコンパイラでは)。

したがって、すべて:

float c = (float)a / b;

float c = a / (float)b;

float c = (float)a / (float)b;

同じ結果になります。

于 2008-10-29T04:04:39.393 に答える
1

それから私のような年配の脳障害者がいて、昔ながらの言語を使用しなければならず、無意識のうちに次のようなものを書いています

int a;
int b;
float z;

z = a*1.0*b;

もちろん、これは普遍的なものではなく、ほとんどこのケースにのみ適しています。

于 2008-10-29T05:45:34.900 に答える
1

安全性が重要なシステムに取り組んできたので、偏執的になりがちで、float(a)/float(b) の両方の要素をキャストする傾向があります。コンパイラがどれほど優れていると言われても、公式の言語仕様で詳細が明確に定義されていても。パラノイア: プログラマーの親友!

于 2008-10-29T05:48:01.963 に答える
0

片面または両面をキャストする必要がありますか?答えはコンパイラーによって指示されません。正確で正確なルールを知っている必要があります。代わりに、後でコードを読む人が答えを指示する必要があります。その理由だけで、両側を同じタイプにキャストします。暗黙の切り捨てが十分に表示される可能性があるため、キャストが冗長になる可能性があります。

たとえば、このキャストfloat->intは明らかです。

int a = float(foo()) * float(c); 
于 2008-10-29T11:33:56.040 に答える