浮動小数点計算の計算

ご存じのとおり、C ++では、コンパイル段階で複雑な浮動小数点計算を実行できません。 私はこの迷惑な欠陥を取り除くことを試みることにしました。 使用する目標は、ルートを計算する例です。

typedef RATIONAL(2,0) x; typedef sqrt<x>::type result;
      
      





数値のルートは、コンパイルの段階で計算されます。 数値の表現は2つの整数の比率として保存されるため、値を取得するにはget()メソッドを使用してアクセスする必要があります。

 std::cout << result::get() << std::endl;
      
      



1.41421356



浮動小数点数を保存するには、リレーションを使用します

2つの整数-分数。

 template <int64_t A, int64_t B> struct rational_t { const static int64_t a = A, b = B; static double get() { return (double)a/b; } };
      
      





たとえば、数値2を宣言するには、タイプrational_t <2,1>(最初の2つ)を宣言する必要があります。

 rational_t<3,2> -> 3/2 rational_t<56,10> -> 56/10 = 5.6 rational_t<3,100> -> 3/100 = 0.03
      
      





便宜上、cなRATIONALマクロを使用します。

 #define RATIONAL(A1, A2) rational_t<(int)(A1##A2), pow<10, sizeof(#A2)-1>::value>
      
      





例として考えてください:RATIONAL(12.34)-分数12.34(12ポイント34の100分の1)を宣言します

 1)A1 ## A2は1234の2つの引数を接着します
 2)sizeof(#A2)-1 = sizeof( "34")-1 = 3-1 = 2
 3)pow <10、2> ::値= 10の2乗= 100


したがって、RATIONAL(12.34)はrational_t型を宣言します<1234、100>(つまり、1234/100 = 12.34)

pow関数は次のように書かれています。

 template <int V, unsigned D> struct pow { const static int value = V * pow<V, D - 1>::value; }; template <int V> struct pow<V, 0> { const static int value = 1; };
      
      





rational_tパターンの算術演算について説明します。

分数の追加:a1 / b1 + a2 / b2 =(a1 * b2 + a2 * b1)/(b1 * b2)、したがって:

 template <class R1, class R2> struct plus { typedef rational_t<R1::a * R2::b + R2::a * R1::b, R1::b * R2::b> type; };
      
      





定数を加算すると、分子と分母は絶えず絶対値が大きくなります。 次の添加中のオーバーフローを避けるために、端数を減らす必要があります。 追加機能を書き直します。

 template <class R1, class R2> struct plus { typedef rational_t<R1::a * R2::b + R2::a * R1::b, R1::b * R2::b> type1; typedef typename reduce<type1>::type type; };
      
      





reduceメタ関数は、短縮されていない部分を取り、短縮された部分を返します。 残りの操作は次のとおりです。

 template <class R1, class R2> struct minus { typedef rational_t<R1::a * R2::b - R2::a * R1::b, R1::b * R2::b> type1; typedef typename reduce<type1>::type type; }; template <class R1, class R2> struct mult { typedef rational_t<R1::a * R2::a, R1::b * R2::b> type1; typedef typename reduce<type1>::type type; }; template <class R1, class R2> struct divide { typedef rational_t<R1::a * R2::b, R1::b * R2::a> type1; typedef typename reduce<type1>::type type; };
      
      





比較操作:

 template <class R1, class R2> struct less { static const bool value = (R1::a * R2::b - R2::a * R1::b) < 0; };
      
      





削減の必要性を決定するメタ関数を定義します。 64ビット形式のストレージの最大許容数は(2 ^ 64)^ 0.5 / 2 = 1ll << 31です。したがって、

 template <class R> struct require_reduce { const static int64_t max = (1ll<<31); const static bool value = (R::a >= max) || (R::b >= max); };
      
      





値は、端数を減らす必要があることを意味します。そうしないと、操作中にオーバーフローが発生する可能性があります。

端数は、最初に正確に削減する必要があります-GCD(reduce_accurate)による割り算により、

削減に失敗した場合は、分子を分割して不正確に削減し、

分母は2(reduce_inaccurate)です。

トリム削減のメタ機能を宣言する

 template <bool, class R> struct reduce_accurate;
      
      





不正確な削減。その後、再度削減を試みます。

もちろん、これが必要でない限り、慎重に:

 template <bool, class R> struct reduce_inaccurate { typedef rational_t<(R::a >> 1), (R::b >> 1)> type_; typedef typename reduce_accurate<require_reduce<type_>::value, type_>::type type; };
      
      





不要な場合、ずさんなリダクションは同じ値を返します:

 template <class R> struct reduce_inaccurate<false, R> { typedef R type; };
      
      





きちんとした削減のために、彼らはコメントでGCDを使用することを提案しました。

GCDの計算( gribozavrに感謝):

 template <int64_t m, int64_t n> struct gcd; template <int64_t a> struct gcd<a, 0> { static const int64_t value = a; }; template <int64_t a, int64_t b> struct gcd { static const int64_t value = gcd<b, a % b>::value; };
      
      







リダクション関数は、分子と分母をGCDに分割し、必要に応じて不正確なリダクションを実行します。

 template <bool, class R> struct reduce_accurate { template <bool, class R> struct reduce_accurate { const static int64_t new_a = R::a / gcd<R::a, R::b>::value; const static int64_t new_b = R::b / gcd<R::a, R::b>::value; typedef rational_t<new_a, new_b> new_type; typedef typename reduce_inaccurate<require_reduce<new_type>::value, new_type>::type type; }; };
      
      





正確な削減では不十分な場合は、不正確な削減を実行します。

 template <class R> struct reduce_accurate<false, R> { typedef typename reduce_inaccurate<require_reduce<R>::value, R>::type type; };
      
      







最も興味深いものに移りましょう。Newtonの方法に従ってルートを計算するアルゴリズムを記述します。

この実装は正確であると主張していないため、例として示します。

  : sqrt_eval(p, res, x) { t1 = x/res t2 = res+t1 tmp = t2/2 if (p-1 == 0) return tmp; return sqrt_eval(p-1, tmp, x) }
      
      





rational_tの使用:

 template <int64_t p, class res, class x> struct sqrt_eval { typedef typename divide<x, res>::type t1; typedef typename plus<res, t1>::type t2; typedef typename divide<t2, rational_t<2,1> >::type tmp; typedef typename sqrt_eval<p-1, tmp, x>::type type; }; template <class res, class x> struct sqrt_eval<0, res, x> { typedef res type; };
      
      







 sqrt(x) { res = (x + 1)/2 return sqrt_eval(15, res, x) }
      
      





15-ニュートンのアルゴリズムのステップ数。

 template <class x> struct sqrt { typedef typename divide< typename plus<x, rational_t<1,1> >::type, rational_t<2,1> >::type res; typedef typename sqrt_eval<15, res, x>::type type; }; template <int64_t a> struct sqrt< rational_t<0, a> > { static const int64_t value = 0; };
      
      





例:

 #include <iostream> #include "rational_algo.hpp" int main() { std::cout.precision(15); const double s = rational::sqrt<RATIONAL(2,0)>::type::get(); std::cout << s << std::endl; std::cout << 2-s*s << std::endl; return 0; }
      
      







ファイル全体の例: pastebin.com/ea7S2KTd

プロジェクト: github.com/korkov/rational



UPD:これは更新されたバージョンです。ヒントと修正に感謝します。



All Articles