KEN's .NET

[IL14] さらに型変換!

ホーム > KEN's .NET > [IL14] さらに型変換!

ここでは.NET Frameworkのアセンブリ言語MSIL(Microsoft Intermediate Language)を使ったプログラミングを紹介します。 読者は.NET プログラミング経験があることを想定しています。

1. はじめに

前回、型変換を説明しましたが、符号付き整数の正の数に関する拡大変換、 縮小変換という最も典型的な変換のみの例でした。 今回はさらに符号付き整数の負の数、符号無し整数の拡大変換、浮動小数点数から整数への変換を紹介したいと思います。 conv.i4、conv.i8、conv.u8の3つの型変換命令を使います。 変換対象とする数値によって変換ルールが異なる部分がありますのでそれぞれ確認していきましょう。

2. 型変換命令(再掲載)

前回も型変換命令の一覧を掲載しましたが、今回使用する命令だけ再掲載します。

表1 型変換命令一覧(抜粋)
命令命令書式説明スタック遷移図例外
conv.i4conv.i4int32に変換し、int32としてスタックに置く…, value → …, resultなし
conv.i8conv.i8int64に変換し、int64としてスタックに置く…, value → …, resultなし
conv.u8conv.u8符号なしint64に変換し、int64としてスタックに置く…, value → …, resultなし

3. 負の数の型変換、符号無し整数の型変換

int32の最大値0x7FFFFFFF(2147483647)、最小値0x80000000(-2147483648) を符号付き/符号無し64ビット整数に変換する例を図1(ZeroOrSignExtension.il)に掲載しました。 実行結果も合わせて図2に掲載しています。

図1 32ビット→符号付き/符号無し64ビットの型変換プログラム
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
.assembly ZeroOrSignExtension {}
.method public static void main()
{
    .entrypoint
    .maxstack 2

    ldstr "(1) 変換なし(10進数表示) 0x7FFFFFFF → {0}"
    ldc.i4 0x7FFFFFFF
    box int32
    call void [mscorlib]System.Console::WriteLine(string, object)

    ldstr "(2) 変換なし(10進数表示) 0x80000000 → {0}"
    ldc.i4 0x80000000
    box int32
    call void [mscorlib]System.Console::WriteLine(string, object)

    ldstr "(3) 32bit→64bit         0x7FFFFFFF → {0:X16}"
    ldc.i4 0x7FFFFFFF
    conv.i8
    box int64
    call void [mscorlib]System.Console::WriteLine(string, object)

    ldstr "(4) 32bit→64bit         0x80000000 → {0:X16}"
    ldc.i4 0x80000000
    conv.i8
    box int64
    call void [mscorlib]System.Console::WriteLine(string, object)

    ldstr "(5) 32bit→符号無し64bit 0x7FFFFFFF → {0:X16}"
    ldc.i4 0x7FFFFFFF
    conv.u8
    box int64
    call void [mscorlib]System.Console::WriteLine(string, object)

    ldstr "(6) 32bit→符号無し64bit 0x80000000 → {0:X16}"
    ldc.i4 0x80000000
    conv.u8
    box int64
    call void [mscorlib]System.Console::WriteLine(string, object)
    ret
}

(1) 変換なし(10進数表示) 0x7FFFFFFF → 2147483647
(2) 変換なし(10進数表示) 0x80000000 → -2147483648
(3) 32bit→64bit         0x7FFFFFFF → 000000007FFFFFFF
(4) 32bit→64bit         0x80000000 → FFFFFFFF80000000
(5) 32bit→符号無し64bit 0x7FFFFFFF → 000000007FFFFFFF
(6) 32bit→符号無し64bit 0x80000000 → 0000000080000000
図2 図1のサンプルの実行結果

ソースをざっと見たら、図2の実行結果を見てください。

  • 実行結果の(1)、(2)は0x7FFFFFFF、0x80000000に対する10進数表記を確認してもらうために表示しているだけです。
  • (3)は前回でも行った正の数の拡大変換ですので、特に不思議な点はありません。
  • (4)は負の数の拡大変換ですが、正の数は増えた上位バイト分のすべてのビットに0がセットされているのに対して、 負の数は増えた上位バイト分のすべてのビットに1がセットされている(FF=1111)ことを確認できます。 これは符号付き整数では、拡大変換時に増えたバイトに対して符号ビットを埋めることになっているからです。 これを符号拡張(sign-extension)といいます。
  • 符号ビットについて補足しておくと、符号付き整数のデータの最上位1ビットのことで、正/負を区別するため正の数の場合は0、負の数の場合は1がセットされています。
  • ちなみに(4)で変換結果「FFFFFFFF80000000」は10進数表現で-2147483648であり、 変換前の32ビット時の10進数と値の内容は変わっていません。
  • (5)、(6)は符号無し64ビットへの変換ですので、元の32ビットデータも符号無しであるかのように扱われます。 また、拡大変換により増える上位バイトはすべてのビットが0にセットされます。これを0拡張(zero-extenstion)といいます。

4. 浮動小数点数→整数の型変換

浮動小数点数の正の数と負の数を整数に型変換する例を図3(FloatToInteger.il)に掲載しました。 実行結果も合わせて図4に掲載しています。

図3 浮動小数点数を整数に型変換するプログラム
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.assembly FloatToInteger {}
.method public static void main()
{
    .entrypoint
    .maxstack 2

    ldstr "(1) float32 1.6 → int32? {0}"
    ldc.r4 1.6
    conv.i4
    box int32
    call void [mscorlib]System.Console::WriteLine(string, object)

    ldstr "(2) float32 -2.6 → int32? {0}"
    ldc.r4 -2.6
    conv.i4
    box int32
    call void [mscorlib]System.Console::WriteLine(string, object)
    ret
}

(1) float32 1.6 → int32? 1
(2) float32 -2.6 → int32? -2
図4 図3のサンプルの実行結果

ソースをざっと見たら、図4の実行結果を見てください。

  • float32の正の数1.6と負の数-2.6をそれぞれint32に変換しています。
  • どちらの場合も小数点以下は切り捨てられています。 つまり、浮動小数点数から整数への変換では0に近づく方向へ丸められます。

5. 学んだこと

  • 符号付き整数への拡大変換は符号拡張、符号無し整数への拡大変換は0拡張により上位バイトが増える
  • 浮動小数点数から整数への変換では小数点以下は0に近づく方向に丸められる

A. サンプルダウンロード


ホーム > KEN's .NET > [IL14] さらに型変換!

[e-mail] yone_ken00@hotmail.com