原書で学ぶ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の最大値を取得する場合、下図のようになります。
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
があります。
この後の節については単なる実例というかサンプルなので省略。