わらばんし仄聞記

南の国で引きこもってるWeb屋さん

原書で学ぶ64bitアセンブラ入門(7)

原書で学ぶ64bitアセンブラ入門(6) - わらばんし仄聞記

上のリンクに続いて、11章、浮動小数点数についての続きです。

Conversion

ある整数長から別の整数長や、ある浮動小数点数長から別の浮動小数点数長。はたまた整数長から浮動小数点数長やその逆といった、つまりはwordからquad-wordへの変換やdouble-wordからdoubleへの変換など、そういった変換についてです。
整数長から整数長への変換は今までに出てきたmov命令を使って成される為、ここでは浮動小数点数を絡めた内容について扱います。

Converting to a different length floating point

ざっと挙げると以下のパターンがあります

  • 1つのfloatからdoubleへの変換
  • 2つのpackされたfloatからdoubleへの変換
  • 1つのdoubleからfloatへの変換
  • 2つのpackされたdoubleからfloatへの変換

packされた値の場合、doubleは2つしか入らないのでそれにつられてfloatも2つまでであることはどちらにも共通ですね。
また、これらはいずれもソースにはSSEレジスタかメモリのどちらかが使える一方、デスティネーションにはSSEレジスタしか使用できません。
命令の対応表については下記の通りです。

float -> double double -> float
scalar cvtss2sd cvtsd2ss
packed cvtps2pd cvtpd2ps

実例はこんな感じになります

cvtss2sd    xmm0, [a]       ; get a into xmm0 as a double
cvtsd2ss    xmm0, xmm0      ; convert to float

Converting floating point to/from integer

浮動小数点数から整数への変換、またその逆についてです。
これについてはpackedでの扱いは無いようです。命令は以下の感じ。

float double
xmm -> int cvtss2si cvtsd2si
int -> xmm cvtsi2ss cvtsi2sd

表にはxmmとは書きましたが、整数から浮動小数点数への変換の場合、ソースには汎用レジスタかメモリが使えます。
実際の使い方はこんな感じになります

cvtss2si    eax, xmm0       ; convert to dword integer
cvtsi2sd    xmm0, rax       ; convert qword to double
cvtsi2sd    xmm0, dword [x]     ; convert dword integer

Floating point comparison

浮動小数点数の比較についてです。

IEEE754の仕様では"Not a Number"、つまりNaNについては2つの型があります。quiet NaN(QNaN)とsignaling NaN(SNaN)です。
SNaNは発生すると常に例外を起こし、QNaNは例外を起こさずにその値が安全に伝播していきます。

さて、浮動小数点数での比較の場合、その比較がorderedであるかunorderedであるかによって挙動が変わります。orderedでの比較の場合、オペランドにQNaNかSNaNがあると例外を起こし、unorderedでの比較の場合はSNaNの場合のみ例外を起こします。表にするとこんな感じに。

ordered unordered
QNaN 例外
SNaN 例外 例外

尚、gccコンパイラはunorderedを使うので、QNaNがオペランドに渡された場合でも例外を起こしません。gccも使ってるということでunorderedについて見てみると、floatを比較するにはucomiss命令を使い、doubleを比較するにはucomisdを使います。
これらの比較命令を使うにあたって、第一オペランドはXMMレジスタでなければなりませんが、第二オペランドはXMMレジスタかメモリが使えます。また、これらの比較をする事で各種フラグがセットされることになります。

比較後の条件ジャンプについても、整数での比較後に使っていたjgeなどとは異なる命令を使うことになります。ここで使う命令は jb jbe ja jae となります。aとbはそれぞれ、aboveとbelowの意味のようです。詳細はググれば出てくるでしょうからそちらにお任せ。

Mathematical functions

SSE命令は最小と最大の計算、四捨五入、平方根の計算、逆数平方根についての浮動小数点数関数を持ちます。この節はそれらについて扱います。

Minimum and maximum

最小、最大を計算する命令についてです。一見、命令の名前からしてpackedデータとして複数持つ値の内、最小や最大を計算してくれるのかと自分は思ってしまいましたが、実際には2値の比較しかしてくれません。packedデータの扱いについては後ほど。

とりあえずスカラー用命令については下記のパターンがあります。

float double
最小値 minss maxss
最大値 minsd maxsd

これについてもデスティネーションはXMMレジスタのみで、ソースはXMMレジスタかメモリが使えるという点は相変わらずです。
packedデータについて、命令語のパターンは今までと同様の変化で使えます。scalarの意味のsであったところをpに変えるだけですね。

さて、packedの場合、packされている2要素間で、それぞれのpack値間で比較をして、それらの大きい方または小さい方の値をデスティネーションのpack値へ入れることになります。packedなfloatの最大値を取得する場合、下図のようになります。

f:id:warabanshi21:20140522012330p:plain

rounding

丸めの対象としてはここまでと同様、floatかdouble、scalarかpackedかの組み合わせに拠る4通りの命令があります。
命令はそれぞれ、roundss roundps roundsd roundpdとなります。
これらの命令は3つのオペランドを取り、この3つめは丸めのモード指定です。モードのパターンは下記のようになります。

mode 丸め方法
0 最近接遇数丸め
1 正の無限大への丸め
2 負の無限大への丸め
3 0への丸め

丸め方法の日本語については 端数処理 - Wikipedia を元にしました。
正直この日本語での記述を見たときは1,2の意味がわからなかったですが、これの英語版を見ると

日本語版wiki 英語版wiki
正の無限大への丸め toward +∞
負の無限大への丸め toward −∞

となっており、それぞれ、正の無限大、負の無限大へ近づく方向へ丸めるという意図がわかります。
最近接遇数丸めは

端数が0.5より小さいなら切り捨て、端数が0.5より大きいならは切り上げ、端数がちょうど0.5なら切り捨てと切り上げのうち結果が偶数となる方へ丸める。

というもので、銀行家がよく使った丸め方でもあるそうで、「銀行家の丸め(banker's rounding)」とも呼ばれるそうです。説明の実例を挙げてみると

  • 5.9 -> 6
  • 5.2 -> 5
  • 5.5 -> 6
  • 4.5 -> 4

てな感じで、0.5の値について挙動が分かれるパターンの様です。

Square roots

square rootを求める命令があるってだけの話です。ソースとデスティネーションに使えるレジスタ、メモリの制約やscalar、packedについての変化など、今までの命令と同じです。
命令語は sqrtss sqrtps sqrtsd sqrtpd があります。

この後の節については単なる実例というかサンプルなので省略。