C# 加算演算子

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
tk

C# 加算演算子

#1

投稿記事 by tk » 15年前

C++ で書かれていたソースを C# に移植(そのままソースをコピーしただけ)したのですが
以下に書くソースでエラーがでました。(例として書きます)

***************************
ushort a;
ushort b;
ushort c;

a = 1;
b = 2;

c = a + b;
***************************

の「c = a + b;」のところで、
「型 'int' を 'ushort' に暗黙的に変換できません。明示的な変換が存在します。(cast が不足していないかどうかを確認してください)」
というエラーが出ました。

C++ の時はエラーがでいなかったのですが、C# でコンパイルするとエラーがでてします。
(もちろんキャストしてやるとエラーは消えますが)

ちょっと調べてみたのですが、C# にある加算演算子が、
・int operator +(int x, int y)
・long operator +(long x, long y)
・uint operator +(uint x, uint y)
・ulong operator +(ulong x, ulong y)
しかないことが分かりました。
(簡単に調べただけなので間違ってるかもしれません)


そこで質問です。

質問1
16bit(8bitも?)同士の演算では、絶対「int operator +(int x, int y)」が使われる?

質問2
c++ の時はなぜエラーがでないのか。

質問3
なぜ C# の加算演算子は上記の4つしかないのか。
(.NET Frameworkのアーキテクチャ的な問題?)


以上が質問です。
漠然とし質問で申し訳ないですが、
分かる方がいましたら、是非回答のほうお願い致します。


環境
Window XP
VS2008
.NET Framework 3.5

バグ

Re:C# 加算演算子

#2

投稿記事 by バグ » 15年前

>>質問1
>> 16bit(8bitも?)同士の演算では、絶対「int operator +(int x, int y)」が使われる?

そうです。


>> 質問2
>> c++ の時はなぜエラーがでないのか。

暗黙的型変換が行われるからでしょう。
ちなみにVB.NETでもエラーはおきません(^_^;)


>>質問3
>>なぜ C# の加算演算子は上記の4つしかないのか。
>>(.NET Frameworkのアーキテクチャ的な問題?)

仕様だからでしょうね…。

バグ

Re:C# 加算演算子

#3

投稿記事 by バグ » 15年前

追記です。
前記の現象を解決するには
ushort a;
ushort b;
ushort c;

a = 1;
b = 2;
c = (ushort)((int)a + (int)b);



こうするか…もしくは

ushort a = 1;
ushort b = 2;
ushort c = (ushort)(a + b);
こうするしかないようです。

softya

Re:C# 加算演算子

#4

投稿記事 by softya » 15年前

私も気になったので調べてみました。

>質問1 16bit(8bitも?)同士の演算では、絶対「int operator +(int x, int y)」が使われる?
どうやら、その様です。

>質問2 c++ の時はなぜエラーがでないのか。
C++/CLIやVB.NETは暗黙的にキャストしてくれるみたいです。
MSILコードを見るとソースコードに書いていなくても自動挿入されているとのこと。

>質問3 なぜ C# の加算演算子は上記の4つしかないのか。
MSILレベルで4つしか実装されていないためと思われます。
なぜ4つだけなのかはかなり疑問ですね。基本的にこの4つを優先して使ってくれという意味なのか?
ちなみに++演算子は、ちゃんとushortが用意されている様です。

tk

Re:C# 加算演算子

#5

投稿記事 by tk » 15年前

>バグさん
>softyaさん

回答ありがとうございます。
なるほどVB.NETでもエラーは出ないんですね~。
自分は、まだVBさわったことなくて(汗



> C++/CLIやVB.NETは暗黙的にキャストしてくれるみたいです。
> MSILコードを見るとソースコードに書いていなくても自動挿入されているとのこと。

コンパイラが自動的にキャストしてくれるんですね。



C#だと、なぜエラーで出すんでしょう。
明示的にオーバーヘッドしてる箇所を知らせるためとかだったり・・・?

YuO

Re:C# 加算演算子

#6

投稿記事 by YuO » 15年前

CやC++でもunsigned short同士の加算演算子がないのは同じです。
ref) ISO/IEC 9899:1999 6.3.1.1 Boolean, characters, and integers
ref) ISO/IEC 14882:2003 4.5 Integral promotions
Integer Promotionが発生するため,C/C++でもint/unsinged int型同士の加算として扱われます。

ちなみに,VS2008付属のC#仕様書にも,変換が行われると書いてあります。
さらに,こちらはどの演算子で行われるかまで,書かれています。
これによると,ushort + ushortは上位変換によってint + intとみなされます。
この結果はint型になります。
ref) C# 言語仕様 Version 3.0 7.2.6.2 二項数値上位変換


で,CやC++では,代入先の型が代入元の型より表現可能な値の範囲が小さい場合,自動的に変換が発生します。
ref) ISO/IEC 9899:1999 6.3.1.3 Signed and unsigned integers
ref) ISO/IEC 14882:2003 4.7 Integral conversions
このため,C/C++ではunsigned short同士の加算の結果をunsigned shortに代入する事ができます。

それに対して,C#において表現可能な範囲が縮小されるような変換は明示的に行う必要があります。
ref) C# 言語仕様 Version 3.0 6.1.2 暗黙の数値変換
ref) C# 言語仕様 Version 3.0 6.2.1 明示的な数値変換の一覧表
このため,ushort + ushortはintなので,ushortへは明示的な変換が必要になります。
さらに,checkedコンテキスト中では例外が発生する可能性があるため,
確実にC/C++と同等の効果が欲しい場合,unchekced演算子を使う必要があります。

YuO

Re:C# 加算演算子

#7

投稿記事 by YuO » 15年前

ちなみに,ジェネリックとテンプレートでテストしてみました (すべてVS2008)。
・C++
int
・C++/CLI
System.Int32
・C#
System.Int32
・Visual Basic
System.UInt16 (Strict On/Strict Offともに)

たいちう

Re:C# 加算演算子

#8

投稿記事 by たいちう » 15年前

ご教示お願いします。

C/C++について、YuOさんは、
unsigned short同士の加算演算子がないので、unsigned int型の演算になる。
と書いてますよね?ちょっと実験してみました。

# VC++ .NET 2003
# 警告レベルは4(MAX)
{
	unsigned short x = 2;
	unsigned short y = 3;
	unsigned short z = x + y; // 警告なし
	cout << z << endl;
}

{
	unsigned short x = 2;
	int y = 3;
	unsigned short z = x + y; // warning C4244: '初期化中' : 
	                          // 'unsigned int' から 'unsigned short' に変換しました。
	                          // データが失われているかもしれません
	cout << z << endl;
}
警告レベル4では、「'unsigned int' から 'unsigned short' に変換」する場合に警告が出ますので、
前半が警告なしということは、unsigned short同士の加算の結果がunsigned intに
ならなかった事を現しているのではないでしょうか?

つまり、VC2003の仕様とISO/IEC 14882:2003が異なる、と理解してよいでしょうか?

たかぎ

Re:C# 加算演算子

#9

投稿記事 by たかぎ » 15年前

> C/C++について、YuOさんは、
> unsigned short同士の加算演算子がないので、unsigned int型の演算になる。
> と書いてますよね?

そうとは書かれていませんし、実際そうではありません。

C/C++の加算演算子の場合、両辺のオペランドに対して「通常の算術型変換」が行われます。
オペランドが汎整数型の場合、通常の算術型変換の第1ステップは汎整数拡張(整数拡張、汎整数昇格)です。
汎整数拡張では、unsigned short型は、int型でunsigned short型の全表現範囲を網羅できるのであればint型に、そうでなければunsigned int型に変換されることになります。
具体的には、sizeof(short) < sizeof(int) の環境であればunsigned short → intに、sizeof(short) == sizeof(int)の環境であればunsigned short → unsigned intに変換されます。

たいちう

Re:C# 加算演算子

#10

投稿記事 by たいちう » 15年前

たかぎさん

> そうとは書かれていませんし、実際そうではありません。

ご指摘ありがとうございます。
「unsigned short同士の加算演算子がないので、int型かunsigned int型の演算になる。 」
ということですね。私が勝手に解釈してしまいました。


それと別の修正があります。yの型と警告の型が一致していませんでした。コピペミスです。
{
	unsigned short x = 2;
	unsigned int y = 3;
	unsigned short z = x + y; // warning C4244: '初期化中' :
	                       // 'unsigned int' から 'unsigned short' に変換しました。
	                          // データが失われているかもしれません
	cout << z << endl;
}

tk

Re:C# 加算演算子

#11

投稿記事 by tk » 15年前

> YuOさん

> さらに,checkedコンテキスト中では例外が発生する可能性があるため,
> 確実にC/C++と同等の効果が欲しい場合,unchekced演算子を使う必要があります。

今回は特にオーバーフローとか気にしなくていいのでcheckedは使わない予定です。
checkedを使う際は気をつけます。



これでこの質問は解決とさせていただきます。
回答者の皆様ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る