上記の例は、扱う数値がどのデータ型にも収まるほど小さかったので、問題なく処理できた。だが、いつも数値が十分に小さいわけではない。桁数が大きいデータを、それが収まりきらないほど桁数が小さいデータ型の変数に入れてしまう場合もあるだろう。実際に、キャストを使えば、それは可能である。だが、桁数が足りない以上、もとの値を保つことは不可能である。
この状況を体験するために、List 7-3のサンプル・ソースに、非常に大きい値を入れて試してみよう。List 7-4を見てほしい。
1: using System;
2:
3: namespace Sample004
4: {
5: class Class1
6: {
7: [STAThread]
8: static void Main(string[] args)
9: {
10: long l = 1844674407370955161;
11: int i = (int)l;
12: short s = (short)i;
13: sbyte b = (sbyte)s;
14: Console.WriteLine("{0},{1},{2},{3}",b,s,i,l);
15: }
16: }
17: }
結果はFig.7-4のようになる。一目瞭然である。同じ値を示していない。しかも、正数だったはずが負数扱いされてしまう場合まである。
10進数ではバラバラの値に見えるが、内部的には2進数表現の値の桁数を切りつめただけのものである。2進数表記を意識して使うのでなければ、このような状況が発生するのは避けるようにプログラミングしなければならない。キャストをなるべく使わないように書けば、このような問題をおのずから回避できる。キャストは最後の手段なので、最初からキャストに頼らず、どうしてもキャストが必要なところだけ使うように頑張ろう。
データ変換は、整数だけでなく、実数でも必要とされる。C#には、桁数の多いdoubleと桁数の少ないfloatという2種類の浮動小数点実数のデータ型がある。桁数の少ないfloatの値を桁数の多いdoubleに代入するのは問題ないが、これは誰でもすぐ分かることなのでサンプル・ソースは割愛する。逆に、桁数の多いdoubleの値を桁数の少ないfloatに代入する場合は、整数と同じくキャストを必要とする。実際にキャストしてみた例をList 7-5に示す。
1: using System;
2:
3: namespace Sample005
4: {
5: class Class1
6: {
7: [STAThread]
8: static void Main(string[] args)
9: {
10: double d = 0.123456789012345;
11: float f = (float)d;
12: Console.WriteLine("{0},{1}",d,f);
13: }
14: }
15: }
これを実行するとFig.7-5のようになる。
Fig.7-5を見ると分かるとおり、桁数の少ないデータ型に変換されたときに切り捨てられるのは、下の桁である。つまり、大まかな値は変化しない。精度が落ちるだけである。その意味で、整数をキャストする場合に比べれば比較的被害は少ない。
誤差の出ない小数計算を行うなら、floatやdoubleより10進数で処理するdecimal型が有利である。それでは、さっそく、decimal型の簡単なプログラムを書いてみよう。List 7-6を見てほしい。
1: using System;
2:
3: namespace Sample006
4: {
5: class Class1
6: {
7: [STAThread]
8: static void Main(string[] args)
9: {
10: decimal e = 0.123456789;
11: Console.WriteLine("{0}",e);
12: }
13: }
14: }
ところが、このソース・コードは、コンパイルを通らない。Fig.7-6のようなエラーが発生する。
q:\awrite\gh\cs\wk2\samples\chapt7\sample006\class1.cs(10,16): error CS0664: 型
double のリテラルを暗黙的に型 'decimal' に変換することはできません。'M' サフィックスを使用して、この型のリテラルを作成してください。
これは、何げなく書いた「0.123456789」という値が、実はコンパイラ内部でdouble型の値として認識されていることにより発生したエラーである。つまり、コンパイラは、10行目の代入を、double型の値をdecimal型に代入すると認識して、エラー扱いしたのである。もちろん、キャストを用い、「(decimal)0.123456789」と書いてエラーを避けることはできるが、せっかく誤差を避けるためにdoubleを使っていないのに、ここでdoubleが入り込むのは美しくない。そこで、List 7-7のように記述する。
1: using System;
2:
3: namespace Sample007
4: {
5: class Class1
6: {
7: [STAThread]
8: static void Main(string[] args)
9: {
10: decimal e = 0.123456789m;
11: Console.WriteLine("{0}",e);
12: }
13: }
14: }
ここで重要なポイントは、「0.123456789」の後ろに「m」を付けて、「0.123456789m」と記述したことである。この「m」は、その値がdecimal型だよ、ということをコンパイラに伝える機能を持った文字である。このほかに、「u」は符号なし、「l」はlong型、「f」はfloat型、「d」はdouble型、といった、型を示す機能を持った文字を数値の後ろに付けることができる。無意味なキャストを減らすためにも、これらの文字は有効である。これを実行した結果はFig.7-7のようになる。
Copyright© Digital Advantage Corp. All Rights Reserved.