C / C ++を効率的に、一定の精度で小数点以下を切り上げ
-
16-09-2019 - |
質問
私は次のことを最適化しようとしています。コード怒鳴るがこれを行います:
= 0.775およびI => 0.78
次に精度2 DPを必要とする場合 最後の桁が5である場合は、基本的には、それはそれ以外の場合にはない、上向きに次の桁を四捨五入します。
私の問題は、値が0.44999999343として保存されるように1 decimalpoint 0.5、...及びてSetPrecision 0.45 doesntのラウンドが0.4に丸められていることであった。
ザッツなぜてSetPrecisionが正しく切り上げるために少量を追加し、それは本当に5で終わるならば、高いsetprecision(p+10)
となることを余儀なくされます。
一度行わ、それは列Bと比較して結果を返します。問題は、この機能は、プログラムのクローを作り、数十億回と呼ばれています。これを最適化し、コード内のどのような機能がマシンにとても重い/書き換えする方法上の任意のより良いアイデア?
bool match(double a,string b,int p) { //p = precision no greater than 7dp
double t[] = {0.2, 0.02, 0.002, 0.0002, 0.00002, 0.000002, 0.0000002, 0.00000002};
stringstream buff;
string temp;
buff << setprecision(p+10) << setiosflags(ios_base::fixed) << a; // 10 decimal precision
buff >> temp;
if(temp[temp.size()-10] == '5') a += t[p]; // help to round upwards
ostringstream test;
test << setprecision(p) << setiosflags(ios_base::fixed) << a;
temp = test.str();
if(b.compare(temp) == 0) return true;
return false;
}
解決
私はAPIを一切呼び出していないと、ASMのカップルダースラインよりも何もない整数平方根サブルーチンを書いた - そしてそれはまだだけで5000万SqRoots /秒を行うことができます(これは... 5年ほど前でした) 。
私が作っているポイントは、コールの数十億のためのつもりなら、でも、今日の技術は、チョークしようとしていることです。
しかし、あなたが本当にそれをスピードアップするための努力をしたい場合、など人間的に可能な限り多くのAPIの使い方を削除します。これは、代わりにライブラリがあなたのためにそれを行うせるの、手動でAPIのタスクを実行する必要があります。具体的には、ストリーム操作のいずれかのタイプを削除します。これらは、この文脈で汚れよりも遅いです。あなたは本当にそこに即興する必要があります。
唯一のことは、それはあなたができるようカスタムASMとC ++のように多くの行を交換することで後に行うために、左 - しかし、あなたはそれについての完璧主義者でなければならないでしょう。だけでなく、CPUのキャッシュやスタック領域のすべてのバイト - あなたはすべてのCPUサイクルをフルに活用して登録されていることを確認してください。
これらは、はるかに多くのASM-フレンドリーで、はるかに効率的ですとしてあなたは、整数値の代わりに、浮動ポイントを使用して検討することができます。あなたは右の上、小数点のすべての方法を移動するには(あなたがあなたのロジックを形成することを決定した方法に応じて、または10 ^ p)は10 ^ 7で数を乗算する必要があると思います。そして、あなたは安全に基本的な整数に浮動小数点を変換することができます。
あなたは残りの部分を行うには、コンピュータのハードウェアに依存する必要があります。
<--Microsoft Specific-->
私はまた、C ++の識別子は(ドニーDeBoerが述べたように、静的なものを含む)を直接あなたのC ++コードにネストされたASMブロックからアクセス可能であることを追加します。これは、インラインASM風になります。
<--End Microsoft Specific-->
他のヒント
あなたは数字のために欲しいものに応じて、あなたの代わりに浮動小数点の固定小数点数を使用する場合があります。クイック検索はこのにまでなります。
私はあなただけの「%1.2f」(百、1.3fの千分のなど)のようなものではsnprintfなど、百に精度のために結果を何千ものための0.0005を0.005を追加し、文字列を比較することができると思います。あなたは、テーブル-IZEにできることや、このロジックをパラメータ化する必要があります。
あなたはそれが何度もそれを割り当てていないようにだけ、それは二重のT []静的作ることによって、あなたの投稿コードにいくつかの主要なサイクルを節約することができます。
の代わりにこれを試してください:
#include <cmath>
double setprecision(double x, int prec) {
return
ceil( x * pow(10,(double)prec) - .4999999999999)
/ pow(10,(double)prec);
}
これはおそらく高速です。多分同様にそれをインライン化しようと、それは助けにはならない場合には、傷つけることがあります。
それがどのように動作するかの例:
2.345* 100 (10 to the 2nd power) = 234.5
234.5 - .4999999999999 = 234.0000000000001
ceil( 234.0000000000001 ) = 235
235 / 100 (10 to the 2nd power) = 2.35
0.4999999999999があるため、32ビットのシステム上で二重のC ++のための精度を選択しました。あなたは64ビットプラットフォームにしている場合は、おそらくより多くのナインが必要になります。あなたは32ビットシステム上で、さらにナインを増やす場合は、オーバーフローしてアップするのではなく切り捨て、I。電子。 234.00000000000001は、(私の)32ビット環境では、二重で234に切り捨てられます。
浮動小数点(不正確な表現)を使用すると、真数に関するいくつかの情報を失ってしまったことを意味します。あなたは、単にファッジ値を追加することにより、二重に格納された値を「修正」することはできません。それは(0.45のように)一定の場合を修正するかもしれないが、それは他の例を中断します。あなたは切り捨てて表示しているはずの数字を切り上げてしまいます。
ここで関連記事があります: http://www.theregister.co.uk/2006/08/12 / floating_point_approximation / の
私はあなたが本当にし何を意味するかを推測で取っています。私は、あなたが文字列の一部の精度に二重の10進表現が含まれているかどうかを確認しようとしている疑いがあります。おそらくそれは、算術クイズ番組だと、あなたは、ユーザーの応答が本当の答えに「十分に近い」であるかどうかを確認しようとしています。そのような場合、二重に文字列を変換すると2倍の差の絶対値は、いくつかの許容範囲内にあるかどうかを確認するために簡単かもしれます。
double string_to_double(const std::string &s)
{
std::stringstream buffer(s);
double d = 0.0;
buffer >> d;
return d;
}
bool match(const std::string &guess, double answer, int precision)
{
const static double thresh[] = { 0.5, 0.05, 0.005, 0.0005, /* etc. */ };
const double g = string_to_double(guess);
const double delta = g - answer;
return -thresh[precision] < delta && delta <= thresh[precision];
}
別の可能性としては、(それがまだ数値だが)文字列に変換する前に、最初の答えを丸めることです。
bool match2(const std::string &guess, double answer, int precision)
{
const static double thresh[] = {0.5, 0.05, 0.005, 0.0005, /* etc. */ };
const double rounded = answer + thresh[precision];
std::stringstream buffer;
buffer << std::setprecision(precision) << rounded;
return guess == buffer.str();
}
これらのソリューションのどちらも、あなたのサンプルコードよりも高速である必要がありますが、私は、彼らはあなたが本当にやりたいかはわからない。
私の知る限り見るようにあなたがチェックされます。
Insted文字列に変更するので、倍増する他の方法や変更の文字列を作ります - (小さなテーブルを使用だけ乗算及びaddionのみadditoins) - 両方の数値をsubstractとsubstractionが適正範囲内にあるかどうかを確認(IF P == 1 => ABS(P-A)<0.05)
ポンドの暗黒時代からの古い時間の開発者のトリック、旧国のシリングとペンスます。
トリック半pennys FO整数として値を格納することでした。 (または任意のあなたの最小単位です)。そして、すべての後続のarithmaticはなどarithimaticと丸め簡単な整数が自分自身の世話をしますです。
あなたのケースでは、あなたがカウントされているものは何でもの200thsの単位でデータを保存するので、 あなたは結果を表示したい時はいつでもフロートvaraibleに200によって、これらの値と除算の単純な整数計算を行います。
私は、実行時の速度のためのあなたの必要性は、おそらく、このそうでない場合は優れたソリューションを排除する、ブーストは、これらの日「のBigDecimal」ライブラリを行います信じ、しかし。
あなたが何をしようとしていることは本当の丸めないように見えます。 0.45は、実際のバイナリ表記で0.45であり、0.44999999343は同じものではありません。
それから1に、2つに、小数点第3位を言って最初の -あなたは、複数の丸めを行う必要がありますすることができます。
問題は、何を達成しようとしているのですか?あなたの一致基準がなければなりません。
abs(a-b) < 10 ** -p
の代わりに?