質問
次のスニペットを与えられた:
#include <stdio.h>
typedef signed long long int64;
typedef signed int int32;
typedef signed char int8;
int main()
{
printf("%i\n", sizeof(int8));
printf("%i\n", sizeof(int32));
printf("%i\n", sizeof(int64));
int8 a = 100;
int8 b = 100;
int32 c = a * b;
printf("%i\n", c);
int32 d = 1000000000;
int32 e = 1000000000;
int64 f = d * e;
printf("%I64d\n", f);
}
MINGW GCC 3.4.5の出力は(-O0)です。
1
4
8
10000
-1486618624
最初の乗算は、(アセンブラー出力による)内部的にINT32にキャストされます。 2番目の乗算はキャストされません。プログラムがIA32で実行されていたのか、それがC標準のどこかで定義されているため、結果が異なるかどうかはわかりません。それにもかかわらず、この正確な動作がどこかで定義されているかどうか(ISO/IEC 9899?)が興味があります。なぜなら、私が手動でキャストする理由をよりよく理解するのが好きだからです(別のアーキテクチャからプログラムを移植するのに問題があります)。
解決
C99標準は、 *
より小さい整数タイプで動作しないでください int
. 。これらのタイプの表現は促進されます int
オペレーターが適用される前。 6.3.1.4パラグラフ2および「整数プロモーション」という単語の多数の発生を参照してください。しかし、これはコンパイラによって生成されたアセンブリの指示に対してやや直交しています。 int
sこれは、コンパイラがより短い結果を計算することが許可されていても高速であるためです(たとえば、結果はすぐに短いタイプのL値に保存されるためです)。
それにかんする int64 f = d * e;
どこ d
と e
タイプです int
, 、乗算は次のように行われます int
同じプロモーションルールに従って。オーバーフローは技術的にです 未定義の動作, 、ここでは2つの補完結果を得ていますが、標準に従って何でも得ることができます。
注:プロモーションルールは、宣伝時に署名されたタイプと署名されていないタイプを区別します。ルールは、より小さなタイプを宣伝することです int
そうでもなければ int
タイプのすべての値を表すことはできません。その場合 unsigned int
使用されている。
他のヒント
問題は、乗算がint32 * int32であり、これはint32として行われ、結果はint64に割り当てられていることです。あなたはほぼ同じ効果を得るでしょう double d = 3 / 2;
, 、整数部門を使用して3で3を分割し、1.0をに割り当てます d
.
それが重要なときはいつでも、表現またはサブエグセンションの種類に注意を払わなければなりません。これには、適切な操作が適切なタイプとして計算されることを確認する必要があります。たとえば、Multiplicandsの1つをINT64または(私の例で)キャストするなど 3.0 / 2
また (float)3 / 2
.
K&R(オリジナル)を読んでください。すべての整数操作は、より大きなものに対してキャストされる(またはキャストされる)変数を伴わない限り、自然整数タイプで行われます。 CHARの操作は、そのアーキテクチャの整数の自然なサイズであるため、32ビットにキャストされます。 2つの32ビット整数の乗算は、32ビットで行われます。なぜなら、それを大きなものにキャストするものは何もないためです(64ビット変数に割り当てるまで、それは手遅れです)。操作を64ビットで発生させたい場合は、片方または両方のINTを64ビットまでキャストします。
int64 f = (int64)d * e;
a * b
INTとして計算され、受信変数タイプにキャストされます(たまたまINTです)
d * e
INTとして計算され、受信変数タイプにキャストされます(たまたまINT64です)
型変数のいずれかが、そのタイプが使用されていたよりも、int(または浮動小数点)よりも大きい場合。ただし、マルチプリで使用されるすべてのタイプがINTまたは小さいため、INTが使用されました。