VC++で拡張倍精度をどうしても使いたい
プログラムメモ | 2010/12/19 Sun 08:25
| VC++で拡張倍精度浮動小数点形式をどうしても使いたい
Visual Stdioでは拡張倍精度浮動小数(80bit)が使えません。
long double と書いても、64bit精度になります。
通常、仮数部53bit(暗黙Bit含め)で不足することはないでしょうが、
どうしても仮数部64bitが必要という場合のサンプルクラスです。
様々なコンストラクタ、四則演算・比較の他、FPUでサポートしている三角関数や平方根を入れました。
Ldoubleクラスを使う前に、Ldouble_init()を呼び出して、FPUに拡張倍精度を指示する必要があります。
このコードは動作しますが、あまり速くありません。
実行速度についてはコンパイラが拡張倍精度をサポートしてくれないと、どうにもなりません。
実行速度をそれほど求めていないが、仮数部64bitの精度が必要であるか、
最大値が10進で300桁以上になる場合(拡張倍精度ではおよそ5000桁まで可能)にのみ有効です。
Win32アプリで拡張倍精度計算を行い、同時に実行速度も求めるなら、VC以外のコンパイラ(C++ Builder等)を利用した方が良いでしょう。
Tags: プログラムメモ
Visual Stdioでは拡張倍精度浮動小数(80bit)が使えません。
long double と書いても、64bit精度になります。
通常、仮数部53bit(暗黙Bit含め)で不足することはないでしょうが、
どうしても仮数部64bitが必要という場合のサンプルクラスです。
様々なコンストラクタ、四則演算・比較の他、FPUでサポートしている三角関数や平方根を入れました。
/* (c) Hundredsoft Corporation 2010 All rights reserved. Ldouble.h - VC 拡張倍精度クラス */ #ifndef LDOUBLE_INCLUDED #define LDOUBLE_INCLUDED #include typedef struct { unsigned char d[12]; } LONG_DOUBLE_STR, *LPLONG_DOUBLE_STR; void Ldouble_init() { unsigned short fcw=0x033f; // round to nearest, 64 bits, exceptions disabled __asm { fldcw [fcw] } } class Ldouble { public: Ldouble() { memset(&m_data, 0, sizeof(m_data)); } virtual ~Ldouble(){} Ldouble(const Ldouble& v) { if (this != &v){ memcpy(&m_data, &v.m_data, sizeof(m_data)); } } Ldouble(const __int64& v) { LPLONG_DOUBLE_STR m = &m_data; __asm { mov eax, [v] fild qword ptr [eax] mov eax, [m] fstp tbyte ptr [eax] } } Ldouble(const unsigned __int64& v) { __int64 t = 0x8000000000000000; int i; for (i=63; i>=0; i--, t>>=1){ if (v & t) break; } if (i < 0){ memset(&m_data, 0, sizeof(m_data)); }else{ t = v; t <<= 63-i; // 仮数部 int ex = i + 16383; // 指数部 memcpy(&m_data, &t, sizeof(__int64)); *(int*)&m_data.d[8] = ex; } } Ldouble(const int& v) { LPLONG_DOUBLE_STR m = &m_data; __asm { mov eax, [v] fild dword ptr [eax] mov eax, [m] fstp tbyte ptr [eax] } } Ldouble(const unsigned int& v) { __int64 i = v; LPLONG_DOUBLE_STR m = &m_data; __asm { lea eax, [i] fild qword ptr [eax] mov eax, [m] fstp tbyte ptr [eax] } } Ldouble(const short& v) { LPLONG_DOUBLE_STR m = &m_data; int i = v; __asm { lea eax, [i] fild dword ptr [eax] mov eax, [m] fstp tbyte ptr [eax] } } Ldouble(const unsigned short& v) { LPLONG_DOUBLE_STR m = &m_data; int i = v; __asm { lea eax, [i] fild dword ptr [eax] mov eax, [m] fstp tbyte ptr [eax] } } Ldouble(const char& v) { LPLONG_DOUBLE_STR m = &m_data; int i = v; __asm { lea eax, [i] fild dword ptr [eax] mov eax, [m] fstp tbyte ptr [eax] } } Ldouble(const unsigned char& v) { LPLONG_DOUBLE_STR m = &m_data; int i = v; __asm { lea eax, [i] fild dword ptr [eax] mov eax, [m] fstp tbyte ptr [eax] } } Ldouble(const double& v) { LPLONG_DOUBLE_STR m = &m_data; __asm { mov eax, [v] fld qword ptr [eax] mov eax, [m] fstp tbyte ptr [eax] } } Ldouble& operator=(const Ldouble& v){ if (this != &v){ memcpy(&m_data, &v.m_data, sizeof(m_data)); } return *this; } // Unary - Ldouble operator-(){ Ldouble v = *this; v.m_data.d[9] ^= 0x80; return v; } Ldouble operator++(){ LPLONG_DOUBLE_STR m = &m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fld1 fadd mov eax, [m] fstp tbyte ptr [eax] } return *this; } Ldouble operator++(int){ Ldouble v = *this; LPLONG_DOUBLE_STR m = &m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fld1 fadd mov eax, [m] fstp tbyte ptr [eax] } return v; } Ldouble operator--(){ LPLONG_DOUBLE_STR m = &m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fld1 fsub mov eax, [m] fstp tbyte ptr [eax] } return *this; } Ldouble operator--(int){ Ldouble v = *this; LPLONG_DOUBLE_STR m = &m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fld1 fsub mov eax, [m] fstp tbyte ptr [eax] } return v; } // Binary operator +=, -=, *=, /= Ldouble& operator+=(const Ldouble& v){ LPLONG_DOUBLE_STR m = &m_data; LPLONG_DOUBLE_STR mv = (LPLONG_DOUBLE_STR)&v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] mov eax, [mv] fld tbyte ptr [eax] fadd mov eax, [m] fstp tbyte ptr [eax] } return *this; } Ldouble& operator-=(const Ldouble& v){ LPLONG_DOUBLE_STR m = &m_data; LPLONG_DOUBLE_STR mv = (LPLONG_DOUBLE_STR)&v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] mov eax, [mv] fld tbyte ptr [eax] fsub mov eax, [m] fstp tbyte ptr [eax] } return *this; } Ldouble& operator*=(const Ldouble& v){ LPLONG_DOUBLE_STR m = &m_data; LPLONG_DOUBLE_STR mv = (LPLONG_DOUBLE_STR)&v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] mov eax, [mv] fld tbyte ptr [eax] fmul mov eax, [m] fstp tbyte ptr [eax] } return *this; } Ldouble& operator/=(const Ldouble& v){ LPLONG_DOUBLE_STR m = &m_data; LPLONG_DOUBLE_STR mv = (LPLONG_DOUBLE_STR)&v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] mov eax, [mv] fld tbyte ptr [eax] fdiv mov eax, [m] fstp tbyte ptr [eax] } return *this; } // compare // this < v: - // this == v: 0 // this > v: + int comp(const Ldouble& v) const{ LPLONG_DOUBLE_STR m = (LPLONG_DOUBLE_STR)&m_data; LPLONG_DOUBLE_STR mv = (LPLONG_DOUBLE_STR)&v.m_data; short sts; __asm { mov eax, [m] fld tbyte ptr [eax] mov eax, [mv] fld tbyte ptr [eax] lea eax, [sts] fcompp fnstsw word ptr [eax] } return ((sts & 0x4100) - 1) ^ 0x3fff; } bool operator==(const Ldouble& v) const{ return (comp(v) == 0); } bool operator!=(const Ldouble& v) const{ return (comp(v) != 0); } bool operator<(const Ldouble& v) const{ return (comp(v) < 0); } bool operator<=(const Ldouble& v) const{ return (comp(v) <= 0); } bool operator>(const Ldouble& v) const{ return (comp(v) > 0); } bool operator>=(const Ldouble& v) const{ return (comp(v) >= 0); } // cast operator double() { LPLONG_DOUBLE_STR m = (LPLONG_DOUBLE_STR)&m_data; double t; __asm { mov eax, [m] fld tbyte ptr [eax] fstp qword ptr [t] } return t; } operator int() { LPLONG_DOUBLE_STR m = (LPLONG_DOUBLE_STR)&m_data; double t; __asm { mov eax, [m] fld tbyte ptr [eax] fstp qword ptr [t] } return (int)t; } //friend Binary operator +,-,*,/ friend const Ldouble operator+(const Ldouble&a, const Ldouble&b){ Ldouble p(a); p += b; return p; } friend const Ldouble operator-(const Ldouble&a, const Ldouble&b){ Ldouble p(a); p -= b; return p; } friend const Ldouble operator*(const Ldouble&a, const Ldouble&b){ Ldouble p(a); p *= b; return p; } friend const Ldouble operator/(const Ldouble&a, const Ldouble&b){ Ldouble p(a); p /= b; return p; } LONG_DOUBLE_STR m_data; }; namespace LD { Ldouble abs(const Ldouble& s){ Ldouble v(s); v.m_data.d[9] &= 0x7f; return v; } Ldouble sin(const Ldouble& s){ Ldouble v(s); LPLONG_DOUBLE_STR m = &v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fsin fstp tbyte ptr [eax] } return v; } Ldouble cos(const Ldouble& s){ Ldouble v(s); LPLONG_DOUBLE_STR m = &v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fcos fstp tbyte ptr [eax] } return v; } Ldouble tan(const Ldouble& s){ Ldouble v(s); double t; LPLONG_DOUBLE_STR m = &v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fptan lea ebx, [t] fstp qword ptr [ebx] fstp tbyte ptr [eax] } return v; } Ldouble atan(const Ldouble& s){ Ldouble v(s); LPLONG_DOUBLE_STR m = &v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fld1 fpatan fstp tbyte ptr [eax] } return v; } Ldouble atan2(const Ldouble& x, const Ldouble& y){ Ldouble v(y); LPLONG_DOUBLE_STR xm = (LPLONG_DOUBLE_STR)&x.m_data; LPLONG_DOUBLE_STR vm = &v.m_data; __asm { mov eax, [xm] fld tbyte ptr [eax] mov eax, [vm] fld tbyte ptr [eax] fpatan fstp tbyte ptr [eax] } return v; } Ldouble sqrt(const Ldouble& s){ Ldouble v(s); LPLONG_DOUBLE_STR m = &v.m_data; __asm { mov eax, [m] fld tbyte ptr [eax] fsqrt fstp tbyte ptr [eax] } return v; } Ldouble pi(){ Ldouble v; LPLONG_DOUBLE_STR m = &v.m_data; __asm { mov eax, [m] fldpi fstp tbyte ptr [eax] } return v; } Ldouble log(const Ldouble& s){ Ldouble v(s); LPLONG_DOUBLE_STR m = &v.m_data; __asm { fldln2 mov eax, [m] fld tbyte ptr [eax] fyl2x fstp tbyte ptr [eax] } return v; } Ldouble log10(const Ldouble& s){ Ldouble v(s); LPLONG_DOUBLE_STR m = &v.m_data; __asm { fldlg2 mov eax, [m] fld tbyte ptr [eax] fyl2x fstp tbyte ptr [eax] } return v; } Ldouble exp(const Ldouble& s){ Ldouble v(s); LPLONG_DOUBLE_STR m = &v.m_data; double half = 0.5; bool sign = ((m->d[9] & 0x80) != 0); m->d[9] &= 0x7f; __asm { fldl2e // log2e mov eax, [m] fld tbyte ptr [eax] // log2e, v fmul // log2e*v fld st(0) // log2e*v, log2e*v lea ebx, [half] fld qword ptr [ebx] // log2e*v, log2e*v, 0.5 fsub // log2e*v, log2e*v-0.5 frndint // log2e*v, (int)(log2e*v-0.5) fxch // (int)(log2e*v-0.5), log2e*v fsub st(0), st(1) // (int)(log2e*v-0.5), log2e*v - (int)(log2e*v-0.5) f2xm1 // (int)(log2e*v-0.5), 2^(log2e*v - (int)(log2e*v-0.5))-1 fld1 // (int)(log2e*v-0.5), 2^(log2e*v - (int)(log2e*v-0.5))-1, 1 fadd // (int)(log2e*v-0.5), 2^(log2e*v - (int)(log2e*v-0.5)) fscale // (int)(log2e*v-0.5), 2^(log2e*v - (int)(log2e*v-0.5)) * 2^(int)(log2e*v-0.5) fstp tbyte ptr [eax] // (int)(log2e*v-0.5) fstp qword ptr [ebx] // } if (sign){ __asm { fld1 mov eax, [m] fld tbyte ptr [eax] fdiv fstp tbyte ptr [eax] } } return v; } Ldouble pow(const Ldouble& x, const Ldouble& y){ Ldouble u(x),v(y); LPLONG_DOUBLE_STR mx = &u.m_data; LPLONG_DOUBLE_STR m = &v.m_data; double half = 0.5; bool xsign = ((mx->d[9] & 0x80) != 0); bool ysign = ((m->d[9] & 0x80) != 0); if (xsign){ int ny = (int)v; Ldouble t = ny; if (t != y){ throw "Invalid value."; } xsign = (ny & 1) ? true : false; } mx->d[9] &= 0x7f; m->d[9] &= 0x7f; __asm { fld1 // 1 mov eax, [mx] fld tbyte ptr [eax] // 1, x fyl2x // log2x mov eax, [m] fld tbyte ptr [eax] // log2x, v fmul // log2x*v fld st(0) // log2x*v, log2x*v lea ebx, [half] fld qword ptr [ebx] // log2x*v, log2x*v, 0.5 fsub // log2x*v, log2x*v-0.5 frndint // log2x*v, (int)(log2x*v-0.5) fxch // (int)(log2x*v-0.5), log2x*v fsub st(0), st(1) // (int)(log2x*v-0.5), log2x*v - (int)(log2x*v-0.5) f2xm1 // (int)(log2x*v-0.5), 2^(log2x*v - (int)(log2x*v-0.5))-1 fld1 // (int)(log2x*v-0.5), 2^(log2x*v - (int)(log2x*v-0.5))-1, 1 fadd // (int)(log2x*v-0.5), 2^(log2x*v - (int)(log2x*v-0.5)) fscale // (int)(log2x*v-0.5), 2^(log2x*v - (int)(log2x*v-0.5)) * 2^(int)(log2x*v-0.5) fstp tbyte ptr [eax] // (int)(log2x*v-0.5) fstp qword ptr [ebx] // } if (ysign){ __asm { fld1 mov eax, [m] fld tbyte ptr [eax] fdiv fstp tbyte ptr [eax] } } if (xsign){ m->d[9] |= 0x80; } return v; } } // end of namespace #endif
Ldoubleクラスを使う前に、Ldouble_init()を呼び出して、FPUに拡張倍精度を指示する必要があります。
このコードは動作しますが、あまり速くありません。
実行速度についてはコンパイラが拡張倍精度をサポートしてくれないと、どうにもなりません。
実行速度をそれほど求めていないが、仮数部64bitの精度が必要であるか、
最大値が10進で300桁以上になる場合(拡張倍精度ではおよそ5000桁まで可能)にのみ有効です。
Win32アプリで拡張倍精度計算を行い、同時に実行速度も求めるなら、VC以外のコンパイラ(C++ Builder等)を利用した方が良いでしょう。
Tags: プログラムメモ
author : HUNDREDSOFT | - | -