ホーム 道しるべ 憩いの広場 濃緑空間 濃緑研の日記

逆数(除算)
ホーム ] 上へ ] [ 逆数(除算) ] 逆数平方根と平方根 ] 処理時間を測ろう ] 配列の扱い・最適化1 ] 最適化2 ]

 


3DNow!に除算(割り算)はありません。

では
c = a/b

はどのように求めるのでしょう?

3DNow!には逆数を求める命令シーケンスがあります。

おもしろいことに、14-15ビット精度でかまわなければ、bの逆数(1/b)の近似値は1命令(2サイクル・レイテンシ)で求まります。

これには内蔵型ROMベースのテーブル引きを使っているとのことです。

さらに精度を高めるにはNewton-Raphson法を繰り返します。

Xi+1 = Xi * (2 - b*Xi)

最初の近似値(X0が14ビットまで正確であれば、24ビット精度は1回のNewton-Raphsonで求まります。

それを実現するのが、以下の命令シーケンスです。

X0 = pfrcp(b)
X1 = pfrcpit1(b,X0)
X2 = pfrcpit2(X1,X0)

これでX2が24ビット精度のbの逆数です。

では、実際に確かめてみましょう。

#include "amd3d.h"
#include <windows.h>
#include <math.h>
#include <stdio.h>

main()
{
    float    a,b,c;
    char    buf[80];

    a = 0.1;
    b = 0.5;
    _asm{
        femms
        movd         mm0,b

        pfrcp         (m1,m0)
        pfrcpit1    (m0,m1)
        pfrcpit2    (m0,m1)         ;mm0 ???         | 1/b

        movd         mm1,a
        pfmul         (m0,m1)         ;mm0             | a/b
        movd         c,mm0
        femms
    }
    sprintf(buf,"%f",c);
    MessageBox(GetDesktopWindow(), buf,"Test 3DNow!",MB_OK);
}

上の例はSISDでしめしたが、2つの値を同時に同じ値で割る場合は、SIMDを利用して下記のように求めます。

#include "amd3d.h"
#include <windows.h>
#include <math.h>
#include <stdio.h>

typedef struct vector {
    float    x,y;
} vector;

main()
{
    vector    va,vc;
    float    b;
    char    buf[80];

    va.x = 0.1;
    va.y = 10;
    b = 3;
    _asm{
        femms
        movd         mm0,b

        pfrcp         (m1,m0)
        pfrcpit1    (m0,m1)
        pfrcpit2    (m0,m1)         ;mm0    ???         | 1/b

        punpckldq     mm0,mm0        ;mm0     1/b        | 1/b

        movq         mm1,va
        pfmul         (m0,m1)         ;mm0    va.y/b     | va.x/b
        movq         vc,mm0
        femms
    }
    sprintf(buf,"%f %f",vc.x,vc.y);
    MessageBox(GetDesktopWindow(), buf,"Test 3DNow!",MB_OK);
}

ここで見慣れない命令punpckldqというものを使っています。
これは、レジスタの右半分に格納された値を左半分にコピーして格納するものです。

本来pfrcp命令は32ビット浮動小数点データ(ソースレジスタ右半分の32ビット)の逆数をデスティネーションレジスタの左右の2つの32ビットに格納すると説明されていますが(日本AMD 3DNow!テクノロジマニュアル Publication#:21928 Rev:C amendment/0 Issue Date: May 1998)、そのような動作が確認できませんでした。