1 名前:デフォルトの名無しさん mailto:sage [2014/10/13(月) 15:13:20.18 ID:yFRqmPNp.net] ■公式 ttp://www.microsoft.com/japan/msdn/vstudio/default.aspx ■前スレ Visual Studio 2008 Part 21 peace.2ch.net/test/read.cgi/tech/1290969016/ ■リンク Visual Studio 2008に搭載された17の新機能 ttp://www.atmarkit.co.jp/fdotnet/special/visualstudio2008_01/visualstudio2008_01_01.html 5000個のバグと戦った、MSが「Visual Studio 2008」RTM出荷 ttp://www.atmarkit.co.jp/news/200711/20/vs.html ■関連 Visual Studio 2013 part4 peace.2ch.net/test/read.cgi/tech/1404333757/ Visual Studio 2012 Part8 peace.2ch.net/test/read.cgi/tech/1392639689/ Visual Studio 2010 Part21 peace.2ch.net/test/read.cgi/tech/1412136476/ Visual Studio 2005 Part 27 peace.2ch.net/test/read.cgi/tech/1291513609/ その他テンプレ>>2-5
161 名前:デフォルトの名無しさん [2018/04/16(月) 00:56:57.36 ID:CadfvnK3.net] WineでPassmarkとHDbenchを試したところ、シングルスレッド性能はWindowsより若干高く、マルチコア性能は半分程度に留まった。 おそらくWine内部処理の都合と言うことにw また、DirectXテストは通らなかった。GDIクラスのみ。
162 名前:デフォルトの名無しさん [2018/04/22(日) 03:49:49.80 ID:CAx4oxnE.net] …って言う訳で、早速Linuxのパフォーマンステスト。 64x64ピクセルの2Dスプライトの表示枚数をカウントして、パフォーマンスを計測してみよう。 ダウンロードは以下のリンクからどうぞ。 upload.saloon.jp/src/up27011.zip Firefoxブラウザの右上にダウンロードマーク(↓)の付いたアイコンをクリックして、ファイル アイコンをクリックすると、zipファイルの入ったフォルダが表示されるので、右クリックして 「 Extract Here 」 を選ぶ。 次に、「 agk_sample 」 と言うフォルダが作成されるので、ダブルクリックする。 フォルダの中に 「 abc 」 と言う拡張子のないファイル、「 media 」 と言うランタイムの入った フォルダが作成されている。 「 abc 」 と言う拡張子のないファイルが、実行ファイルで、これを迷わずダブルクリックすれ ばよいw だいたい2分ぐらいで最大表示枚数が安定する。 プログラムの終了は [ ESC ] キー。 Celeron G1820 + Intel HD無印 で1500枚程度。
163 名前:デフォルトの名無しさん [2018/05/23(水) 19:42:34.90 ID:Au5e7VGg.net] 僕の知り合いの知り合いができたパソコン一台でお金持ちになれるやり方 役に立つかもしれません グーグルで検索するといいかも『ネットで稼ぐ方法 モニアレフヌノ』 CRP7I
164 名前:デフォルトの名無しさん mailto:sage [2018/05/24(木) 10:53:48.52 ID:cPlRxlDn.net] CRP7I
165 名前:デフォルトの名無しさん [2018/05/25(金) 19:20:37.97 ID:TsdeULHr.net] ではではw Intel Pentium G5 CoffeeLake 総合スレ anago.open 2ch.net/test/read.cgi/jisaku/1526884213/
166 名前:デフォルトの名無しさん [2018/07/04(水) 22:48:21.01 ID:gFgZc5FG.net] TJH
167 名前:デフォルトの名無しさん mailto:sage [2018/07/06(金) 12:24:57.09 ID:uTPDH9XV.net] TJH
168 名前:デフォルトの名無しさん mailto:sage [2018/08/23(木) 09:04:57.51 ID:by5uxKdK.net] VC9 SP1 Hotfix For The vector<function<FT>> Crash がダウンロードできないのですが、 どなたかアップロードしていただけないでしょうか? https://blogs.msdn.microsoft.com/vcblog/2008/12/17/vc9-sp1-hotfix-for-the-vectorfunctionft-crash/#9234449
169 名前:デフォルトの名無しさん mailto:sage [2018/09/13(木) 23:46:46.29 ID:MPXnmy/B.net] 起動の仕方で浮動小数点演算に誤差が発生するのだけど、理由が分かる人居ますか? 今のところ、 A. Debugビルドのバイナリをダブルクリックして起動 B. Releaseビルドのバイナリをダブルクリックで起動 C. DebugビルドまたはReleaseビルドでF5で起動 の3種類出来てる。それぞれ微妙な誤差だ。(doubleの下位ビット) AとBの違いについては諦めていたのだが、(なお両方とも/fp:precise) 実はCもあると気づいてしまった。 なお、バイナリをダブルクリックして起動、それにIDEをアタッチしても結果は変わらない。 それぞれAまたはCが出る。 何かIDEの設定を間違っているのだと思うのだけど…
170 名前:デフォルトの名無しさん [2018/09/14(金) 12:43:29.26 ID:5xDSXwp0.net] 0fillしてないんだろ
171 名前:デフォルトの名無しさん mailto:sage [2018/09/14(金) 13:05:12.93 ID:XcO59d50.net] 再現性は100%なので、初期化のミスではない。(と思っている) 演算途中の結果を double なら16進16桁でテキストにダンプしている。 数値だけではないが500MB程度のファイルが出力され、 diff を取ることによりレグレッションテストをしている。 今のところ、A/B/Cの3種類しか出ない。 初期化忘れならこうはならない。(はず)
172 名前:デフォルトの名無しさん mailto:sage [2018/09/14(金) 13:09:49.07 ID:Qw2nkUqU.net] MSのエンジニアを信じるか、再現可能なソースも示さず問題だ問題だ言ってるとこの馬の骨とも知れない奴を 信じるかって言われたら迷うことなく前者を信じるw
173 名前:デフォルトの名無しさん mailto:sage [2018/09/14(金) 14:43:10.29 ID:XcO59d50.net] 俺はIDEのバグだとは思っていない。 何かIDEの設定があって、俺がそれを適切にやってないのだと思っている。 色々見た限り、俺は /fp:precise しか発見出来なかった。 ただしこれは一応適切に設定されている。 他にないかな?ということ。
174 名前:デフォルトの名無しさん [2018/09/14(金) 15:49:43.21 ID:cyu8WCuc.net] IDEじゃなくてコマンドラインからコンパイラで直接コンパイルしても一緒?
175 名前:デフォルトの名無しさん mailto:sage [2018/09/14(金) 16:14:47.18 ID:XcO59d50.net] どういう意味? (どれとどれの違いにフォーカスしろと?) 俺はバイナリに問題があるとは思っていない。 何らかの理由で浮動小数点のモードが切り替えられたりしてないかを疑ってる。 だからIDEの設定とか、そっちを見てる。 Release/Debugのバイナリは当たり前だが違ってる。 /O2と/Od等だ。 ただそれでも /fp:precise の場合は 浮動小数点の演算順序を入れ替えない範囲で最適化される事になっている。 逆アセンブラは見たけどちょっとグチャグチャすぎてよく分からなかった。 (見た範囲では演算順序の入れ替えはないように思えた)
176 名前:デフォルトの名無しさん mailto:sage [2018/09/14(金) 16:25:44.73 ID:2giHKuhd.net]
177 名前:="reply_link">>>167 double型を使っていても、SSEのXMMレジスタなどを使う場合、昔のfld, fst, fmulなどを使う場合より基本的には精度が落ちる。 丸めの方向も四捨五入、正負どちらかの方向への丸めや切り捨てなどの他、確か、精度を気にしない、なんてオプションもあった気がする。ゲームで使うことを想定しているらしい。 SSE用に最適化を掛けると、速度は上がるが精度は落ちるかも知れない。 [] [ここ壊れてます]
178 名前:デフォルトの名無しさん mailto:sage [2018/09/14(金) 17:45:18.51 ID:XcO59d50.net] >>174 今の範囲ではSSEは使っていない(はず) 「拡張命令セットを有効にする」は「設定無し」になっている。(多分デフォのまま) 見た目全部x87が出てたし。 やっぱRelease/Debugの違いから攻めろって感じか? 俺もF5起動で結果が異なるってのは全く予想してなかったし。(ただしこちらの問題だろうけど) 演算部分は場合によってはSSEのアセンブラに差し替えるので、 問題になる場合には、Release/Debugの違いはこれで吸収するつもりだった。 (これは1ヶ月後にやるかも)
179 名前:デフォルトの名無しさん mailto:sage [2018/09/14(金) 17:48:23.40 ID:HVgt1gkj.net] 浮動小数点の誤差を考慮に入れてないというイージーな仕様バグですね。
180 名前:デフォルトの名無しさん [2018/09/14(金) 21:39:03.40 ID:fXySkelb.net] 再現するコードをみないとなにもわかるわけがない 低学歴知恵遅れが書くコードなんかなにをやってるか分からないからな ごちゃごちゃいってないで再現するコードをあげなさい
181 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 06:26:55.94 ID:/OsufeBT.net] 月並みな意見だけど、 「症状を再現できる最小のソースと初期データ、それとコンパイル環境」 まで切り詰めてみるのが早道じゃないかな。 ちょくちょく発生する事例なら、解決策を知ってる誰かが教えてくれてるかと。 週末、より多くの人が質問を見ることを期待して待つ手もあるけど。
182 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 08:56:57.28 ID:9ZmI9OgI.net] より基本に立ち返ろう そもそも誤差はあるのか? 計算結果を何を使ってどう出力しているかだけでもソースを見せてくれ
183 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 13:36:34.65 ID:heijdb7v.net] >>175 x87でも誤差を丸める方法を fpu control word で設定できる事は出来る。 四捨五入と切り捨てなどを切り替えられる。
184 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 13:42:47.95 ID:heijdb7v.net] fpu control word で、丸め方法と、精度の二つを独立に設定できる。 この精度の設定で、メモリ上の表現がdouble型でも、計算時のfpuの内部精度が変わってくる。 普通は64bit doubleに対して、fpu内部では80bitの精度で計算する。仮数部のbit数は、確か、それぞれ、53BIT、64BITだったかな。
185 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 14:05:09.61 ID:heijdb7v.net] Intel® 64 and IA-32 Architectures Software Developer’s Manual [Vol 1] 11.6.8 Compatibility of SIMD and x87 FPU Floating-Point Data Types SSE and SSE2 extensions operate on the same single-precision and double-precision floating-point data types that the x87 FPU operates on. However, when operating on these data types, the SSE and SSE2 extensions operate on them in their native format (single-precision or double-precision), in contrast to the x87 FPU which extends them to double extended-precision floating-point format to perform computations and then rounds the result back to a single-precision or double-precision format before writing results to memory. Because the x87 FPU operates on a higher precision format and then rounds the result to a lower precision format, it may return a slightly different result when performing the same operation on the same single-precision or double-precision floating-point values than is returned by the SSE and SSE2 extensions. The difference occurs only in the least-significant bits of the significand.
186 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 14:14:58.92 ID:heijdb7v.net] >>182 書いてある意味は、 「SSEやSSE2だと、float(32BIT)やdouble(64BIT)のまま計算するが、x87 fpuだと、もっと高い精度であるところの 『double extended-precision floating-point format(拡張倍精度浮動小数点フォーマット:80BIT)』 で計算を実行して、丸めてから、floatやdoubleに戻す。 そのため、SSE/SSE2 と x87 fpuでは結果が変わることがある。 しかし、その場合でも結果の違いは、仮数部の LSB (最も価値の小さいBIT)の1BITにだけ現れる。」 というような事。
187 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 16:09:28.75 ID:aC3C7hdp.net] 揚げ足取りだけどLSBを「最も価値が小さい」って直訳はいかがなものかなw 普通に最下位ビットじゃないの? ところで > the least-significant bits これ何で複数形なのかね
188 名前:デフォルトの名無しさん [2018/09/15(土) 16:14:15.89 ID:AVfR6YnT.net] 2の0乗だから実際価値が低い 上はMVBで
189 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 17:07:56.98 ID:UR1d6CKz.net] >>183 ありがとう。 ただ一応それは知ってて、その上で、IDEの環境設定等を疑っている。 >>178 とはいえ地道に辿って、同様の再現コードを作ることに成功しそうだ。 もう少し調べて、多分今晩か明日午前中に上げる。
190 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 17:12:32.47 ID:heijdb7v.net] >>184 「The difference occurs only in the least-significant bits of the significand.」 これは、SSE の場合の LSB と、x87 FPU の場合の FPU の2種類を頭の中に想定していると思われる。 SSE: a1 = 1.xxxxxxxxz * 10^b x87 : a2 = 1.yyyyyyyyw * 10^c つまり、LSB は、↑のように、z と w で、2つあるので、LSBs という英語になる。
191 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 17:15:04.14 ID:heijdb7v.net] 誤:これは、SSE の場合の LSB と、x87 FPU の場合の FPU の2種類を 正:これは、SSE の場合の LSB と、x87 FPU の場合の 2種類を
192 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 17:18:55.19 ID:heijdb7v.net] >>184 >揚げ足取りだけどLSBを「最も価値が小さい」って直訳はいかがなものかなw >普通に最下位ビットじゃないの? 整数の場合は、「最下位ビット」というと、本当にBITの位置が一番右にあるようなイメージもある。 一方、浮動小数点数の場合は、右にあるとかより、仮数部において、一番価値の小さいビット、というニュアンスを使えたかった。 IEEEでは、それは確かに一番右にあるビットであって、マシン語レベルの表現では、BIT0ではあるのだが。 ニュアンス的に。
193 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 17:29:56.92 ID:aC3C7hdp.net] >>187 それは俺も思ったけど、それならsignificandの方も複数じゃないとおかしいような...
194 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 20:35:40.44 ID:UR1d6CKz.net] 同様の症状を再現出来るようになったので上げる。 完全再現は出来てないが、使い物にはなるはず。興味がある人はよろしく。 症状: 単独で起動した場合と、IDEから起動した場合で、結果が異なる。 環境: VS++2008ExpressEdition 再現方法: 1. 新規プロジェクトを作成。(CLRコンソールアプリケーション。オプション等は全てデフォのまま) 2. mainを以下ソースと差し替える。 3. Releaseビルドで実行する。(Debugビルドでは再現しなかったので注意) 結果: 0.000000, 0x1ff68ddfb62221dd (IDEからの起動(F5)) 0.000000, 0x1ff68ddfb62221de (コマンドプロンプトからの起動) 最後の2bitが異なる。(誤差は1bit) 備考: 俺が遭遇しているのとはやや異なる。(俺の場合、Debugビルドでもこれが発生する) この再現状況だと、単に 「DebugとReleaseでバイナリが異なり、 IDEから起動した場合は常にDebugバイナリを掴んでいるだけでは?」 とも取れるが、こちらで確認した限りはちゃんとReleaseバイナリを掴んでいる。 理由は、 1. Debug/Releaseの両方のバイナリがない状態で実行した場合、 自動的に作成されるのはReleaseバイナリ。(当然だが) 結果は****ddとなる。(ブレークポイント等で止めて確認する) 2. そのReleaseバイナリをコマンドプロンプトから起動すると、****deの結果が得られる。
195 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 20:37:06.43 ID:UR1d6CKz.net] ソース: #include "stdafx.h" #include <math.h> using namespace System; template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug. double norm = 0; for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i]; norm = sqrt(norm); if (regulate) for (int i=0;i<num;i++) r[i] = (T)(r[i]/norm); return norm; } int main(array<System::String ^> ^args) { int count = 16; __int64 inputs_hex[16] = { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1fedb1530240aa54, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1ff0af0d95025bc3, 0x1fc9353df6af376b, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}; double* inputs = (double*)inputs_hex; double norm = calc_norm_and_regulate(count, inputs, false); Console::Write(String::Format("{0:F6}, 0x{1:x16}\r\n",norm, *(__int64*)&norm)); // Release build // 0.000000, 0x1ff68ddfb62221dd from IDE // 0.000000, 0x1ff68ddfb62221de from command prompt return 0; }
196 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 20:42:48.06 ID:UR1d6CKz.net] オプション等(コマンドライン):(全てデフォのままのはずだが一応) C/C++: /GL /D "WIN32" /D "NDEBUG" /D "_UNICODE" /D "UNICODE" /FD /EHa /MD /Yu"stdafx.h" /Fp"Release\test_floating_error4.pch" /Fo"Release\\" /Fd"Release\vc90.pdb" /W3 /nologo /c /Zi /clr /TP /errorReport:prompt /FU "c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll" /FU "c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll" /FU "c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.XML.dll" リンカ: /OUT:"MYPATH\test_floating_error4\Release\test_floating_error4.exe" /INCREMENTAL:NO /NOLOGO /MANIFEST /MANIFESTFILE:"Release\test_floating_error4.exe.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"MYPATH\test_floating_error4\Release\test_floating_error4.pdb" /LTCG /DYNAMICBASE /FIXED:No /NXCOMPAT /MACHINE:X86 /ERRORREPORT:PROMPT パスは$MYPATHと書き換えた。また、投稿の為に適宜改行を入れた。 このプロジェクト名は見れば分かるとおりtest_floating_error4。
197 名前:デフォルトの名無しさん mailto:sage [2018/09/15(土) 20:43:03.84 ID:UR1d6CKz.net] 備考2: なおこの方法で見える calc_norm_and_regulate 関数の『逆アセンブル』結果は 俺の環境での物とコールアドレス以外は一致していることを確認している。 一応、diff結果は以下。 8c8 < 0000000c cmp dword ptr ds:[00752E14h],0 --- > 0000000c cmp dword ptr ds:[007D2E14h],0 10c10 < 00000015 call 676F58B9 --- > 00000015 call 683C58B9 44c44 < 0000006a call FF0455E8 --- > 0000006a call FFD955E8
198 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 02:47:43.28 ID:wIV2HUNW.net] >>191 C++/CLR では、.Net を使っているから、起動方法が違うだけでも、 fpu control word の値や、使うCPU命令がx87 FPUなのか、SSE なのかが違ってくる可能性がある
199 名前:ゥもしれない。 fpu control word は、main()関数に入る前の start up codeの中で 初期化される。 [] [ここ壊れてます]
200 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 03:19:36.22 ID:wIV2HUNW.net] >>192 // Release build // 0.000000, 0x1ff68ddfb62221dd from IDE // 0.000000, 0x1ff68ddfb62221de from command prompt それにしても、随分小さな値だね。ちなみに、浮動小数点表示 の場合の有効数字の桁数を上げたら、どのようになる? 1.xxxe-yy 表示にして。
201 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 03:40:33.66 ID:wIV2HUNW.net] >>192 試しに、ソースの冒頭に #include <stdio.h> を追加してから、 Console::Write(String::Format("{0:F6}, 0x{1:x16}\r\n",norm, *(__int64*)&norm)); の部分を、 printf( "%30.30e, 0x%016X\n", norm, *(__int64*)&norm) ); としてみるとどうなる?
202 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 03:42:01.82 ID:wIV2HUNW.net] >>197 誤: printf( "%30.30e, 0x%016X\n", norm, *(__int64*)&norm) ); 正: printf( "%30.30e, 0x%016X\n", norm, *(__int64*)&norm );
203 名前:デフォルトの名無しさん [2018/09/16(日) 07:27:22.50 ID:SOVIz+sV.net] > 0x1ff68ddfb62221dd(Debug) > 0x1ff68ddfb62221de(Release) VS 2010 VC++ Express でも再現した
204 名前:デフォルトの名無しさん [2018/09/16(日) 07:38:10.70 ID:SOVIz+sV.net] ↓このループを抜けたあと、すでにReleaseビルドとDebugビルドでは normの値に差異が発生してることが確認できた for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i]; ↓この下に(ループ内に)fprintf文を入れるだけで ReleaseビルドとDebugビルドが同じ実行結果になることが確認できた norm += (double)r[i] * (double)r[i]; とりあえずまずこれだけは分かったから 低学歴知恵遅れが書いたウンココードの問題箇所を限定する
205 名前:デフォルトの名無しさん [2018/09/16(日) 07:51:03.41 ID:SOVIz+sV.net] @-1 デフォルト設定(Release) 【コード】 #include "stdafx.h" #include <stdio.h> #include <stdint.h> #include <math.h> int main(array<System::String ^> ^args) { __int64 inputs_hex[16] = { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1fedb1530240aa54, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1ff0af0d95025bc3, 0x1fc9353df6af376b, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 }; double* r = (double*)inputs_hex; double norm = 0; for (int i = 0; i < 16; i++) { norm += (double)r[i] * (double)r[i]; // fprintf(stdout, "[1]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm); } fprintf(stdout, "0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm); return 0; }
206 名前:デフォルトの名無しさん [2018/09/16(日) 07:51:56.62 ID:SOVIz+sV.net] @-1 デフォルト設定(Release) ↓このコードの逆アセンブルコード https://ideone.com/Ryyxkl 【実行結果】 0x0007F2C44DFFF8F2:1.1053482540585106e-308
207 名前:デフォルトの名無しさん [2018/09/16(日) 07:53:20.79 ID:SOVIz+sV.net] @-2 デフォルト設定(Release) #include "stdafx.h" #include <stdio.h> #include <stdint.h> #include <math.h> int main(array<System::String ^> ^args) { __int64 inputs_hex[16] = { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1fedb1530240aa54, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1ff0af0d95025bc3, 0x1fc9353df6af376b, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 }; double* r = (double*)inputs_hex; double norm = 0; for (int i = 0; i < 16; i++) { norm += (double)r[i] * (double)r[i]; fprintf(stdout, "[1]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm); } fprintf(stdout, "0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm); return 0; }
208 名前:デフォルトの名無しさん [2018/09/16(日) 08:02:12.99 ID:SOVIz+sV.net] @-2 デフォルト設定(Release) ↓このコードの逆アセンブルコード https://ideone.com/4j12ib 【実行結果】 ↓実行結果を書き込めないからこっちに書き込んどいた https://ideone.com/gOqtki 0x0007F2C44DFFF8F1:1.1053482540585101e-308
209 名前:デフォルトの名無しさん [2018/09/16(日) 08:05:50.92 ID:SOVIz+sV.net] A-1 最適化無効 (/Od)(Release) ※ コードは@-1(>>201 )と同じ ↓このコードの逆アセンブルコード https://ideone.com/cONCAx 【実行結果】 0x0007F2C44DFFF8F1:1.1053482540585101e-308
210 名前:デフォルトの名無しさん [2018/09/16(日) 08:09:59.78 ID:SOVIz+sV.net] A-2 最適化無効 (/Od)(Release) ※ コードはA-2(>>203 )と同じ ↓このコードの逆アセンブルコード https://ideone.com/kuRt3w 【実行結果】 ※ @-2(>>204 )と同じ 0x0007F2C44DFFF8F1:1.1053482540585101e-308
211 名前:デフォルトの名無しさん [2018/09/16(日) 08:20:27.67 ID:SOVIz+sV.net] @-1、@-2の逆アセンブルの出力結果を比較すると原型をとどめてないぐらいグチョグチョに違う(最適化のせいと考えられる) A-1、A-2の逆アセンブルの出力結果を比較すると差異はほとんどない(Aは両方ともまったく最適化されてないから当然) @-1とA-1の逆アセンブルの出力結果を比較すると原型をとどめてないぐらいグチョグチョに違う(@-1のコード(>>201 )ははげしくウンコ最適化されてると考えられる) @-2とA-2の逆アセンブルの出力結果を比較すると差異はほとんどない(@-2のコード(>>203 )はあまり最適化されてないと考えられる) はっきりいって、これ以上見る気もしないしテキトーだが ウンコみたいな最適化で演算の順序が入れ替わったせいで、誤差が発生しているものと考えられる
212 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 08:28:37.25 ID:zL1WUjLu.net] >>199 以降、 すまん、入れ替わりになるかもしれんが後で確認する。 まず>>195 その他について回答する。 >>195 SSEは /arch:SSE または /arch:SSE2 でないと出ないことになっており、勿論設定はしていない。 また、逆アセンブル結果では x87 命令のみであるのも確認している。 ただ今回の問題は、本当にReleaseビルドのバイナリを逆アセンブルしているか怪しい事だが。 >>196 小さい値なのは偶々だ。 辿って行ってそれが1回目にヒットする入力データだっただけのこと。 >>197 .NETの書式指定はググり難いが以下。 https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings 概ねprintfと同じで、自動的にやってくれるのが増えている。 30桁欲しければ以下。 Console::Write(String::Format("{0:E6}, {0:E30}\r\n",norm)); // 出力は 1.051355E-154, 1.051355436595307800000000000000E-154 なおdoubleは16桁な。(15.9=53*log(10)2) ただ当たり前だが、書式を変えたところで計算結果は変わらないし、 精度の問題には関係ない。
213 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 08:29:03.14 ID:zL1WUjLu.net] >>195 FPU control registor については何故か安定した結果を得られていない。 インラインアセンブラは以下の通りで、 #pragma unmanaged inline void fpu_getcw(unsigned short* cw) { __asm{ fnstcw [cw]; } } #pragma managed これを norm = calc_norm_and_regulate( ... ) の直前/直後に配置して読み出し、 同様にコンソール出力すると、以下となる。 また、IDEで起動した場合は、「レジスタ」で見れる。 なお定義は以下の通り。 [9:8]に対し、 0x00 : 単精度(24bit) 0x01 : reserved 0x10 : 倍精度(53bit) 0x11 : 拡張倍精度(64bit) [11:10]に対し 0x00 : 最近値 0x01 : 切り捨て 0x10 : 切り上げ 0x11 : 0方向への切り捨て
214 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 08:29:57.53 ID:zL1WUjLu.net] >>195 直後のみに配置: 0x027F (倍精度) = Debug(IDE起動)のIDE内表示、Release(IDE起動)のIDE内表示、 0x03a5 (拡張倍精度) = Debug(IDE起動)、Release(IDE起動)、 0x3fdc (拡張倍精度) = Debug(コマンドプロンプト)、 0xf280, 0xf290, 0xf160, 0xf010等、不安定 = Release(コマンドプロンプト) 直前のみに配置: 直後のみと同じ結果。(つまり『何故か』安定している) Release(コマンドプロンプト)は不安定なのも同じ。 直前と直後に配置: 直前側は当然不安定になる。 直後側は「直後のみ」の結果と同じ。(Release(コマンドプロンプト)は不安定なのも同じ) 雰囲気からすると、IDE内表示は当てにならず、 命令自体は rdtsc と同じで非同期に実行されている雰囲気だが、 rdtsc命令の注意書きにある「シリアル化命令ではない」という但し書きが無く、状況は不明。 正直、正しく読み出せているか怪しい。(あてにならない) これらから推測すると、暫定的には以下。 拡張倍精度 = Debug(IDE起動)、Release(IDE起動)、Debug(コマンドプロンプト)、 不明 = Release(コマンドプロンプト) 以上が>>195 その他に対する回答。 これから>>199 その他について確認する。
215 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 12:52:37.56 ID:haV9TZ8e.net] >>210 興味深い結果だ。
216 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 13:21:26.16 ID:haV9TZ8e.net] >>210 >命令自体は rdtsc と同じで非同期に実行されている雰囲気だが、 >rdtsc命令の注意書きにある「シリアル化命令ではない」という但し書きが無く、状況は不明。 >正直、正しく読み出せているか怪しい。(あてにならない) インラインアセンブラを使わずに、 _control87(), _controlfp() : Get and set the floating-point control word. unsigned int _control87( unsigned int new, unsigned int mask ); unsigned int _controlfp( unsigned int new, unsigned int mask ); を使ってみたらどうなる?
217 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 13:31:15.50 ID:Q5j4SiHR.net] win32コンソールなら結果が同じ。 もう理由は分かったのに何が問題なんだ?こんなの何の影響もないだろう。
218 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 13:33:14.92 ID:zL1WUjLu.net] >>199 再現実験ありがとう。 しかし色々問題がある。 1. 俺は起動方法による違いについてフォーカスしているが、 君はRelease/Debugの違いにフォーカスしている。 2. VC++2008では再現しない。(VC++2010では再現する) 3. ソース改変しすぎ。それでは意味がない。 4. >>207 の結論は間違い。 まず問題なのはソースの改変だ。 ループ回数を16回と決め打ちしたことで 8*2 に展開されている。 その結果、元のソース(俺が遭遇した状況)では発生しえないことが発生している。 これでは意味がない。 そして、君の結論は間違いだ。 × > ウンコみたいな最適化で演算の順序が入れ替わったせいで、誤差が発生しているものと考えられる 逆アセンブルを追えば分かるが、演算順序は入れ替わっていない。 原因は、Debugでは fld/fmul/fadd/fstp と毎回64bitに整形されるのに対し、 Releaseでは (fld/fmul/fadd)*8 + fstp と整形が8回に1回と減り、 8回は80bit(拡張倍精度)で演算されるからだ。 (こうなったのは君が16回ループ決め打ちコードに改変したから) ただしIDE上の fpu control registor の値は 相変わらず0x027F(倍精度)となっており、 IDEのこの表示が当てにならない事は分かる。 なおVC++2008では再現しなかった。 俺の環境では、16回決め打ちコードでも 8*2 に展開されず、Debugと同じコードだからだ。 勿論結果も同じだった。
219 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 13:33:48.22 ID:zL1WUjLu.net] >>199 問題は、俺の環境で俺が提供したコード>>192 だと、 同様に展開されないにも関わらず、『起動方法によって』結果が異なってしまう点だ。 俺の環境でのRelease/Debugの逆アセンブル結果のdiffは以下。 17c17 < 0000000c cmp dword ptr ds:[001C2E14h],0 --- > 0000000c cmp dword ptr ds:[00702E14h],0 19c19 < 00000015 call 68302BA9 --- > 00000015 call 683A5AB1 93c93 < 0000015a call FF6C3098 --- > 0000015a call FFCA57E8 98c98 < 0000016f push 0B5311Ch --- > 0000016f push 0D03188h 104,105c104,105 < 00000183 push 4F9D68h < 00000188 call FF6C30A4 --- > 00000183 push 2B71C0h > 00000188 call FFCA57F4 アドレスの変更だけであり、君の結果 「ループ回数を決め打ちしたことによりアンローリングされ、一部の演算がx87精度で計算される」には該当しない。 そして、この状況でも結果が異なってしまうことが問題なのだ。 君は君が勝手に新しく作り込んだ問題に対し、間違った結論でお茶を濁したにすぎない。 君が知っているFPU関連のことはこちらも知っている上で、質問している。
220 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 13:53:17.67 ID:haV9TZ8e.net] >>214 なるほど、全く別の2つの理由で、精度が変わっている可能性(というより多分、確実)があると。 それは以下の2つ: 1. Debug版とRelease版では、最適化の結果、x87 FPU命令の使われ方が変わる。 x87では、メモリに書き戻さずに st(0)〜st(7)レジスタに入っている途中では、 拡張倍精度の80BITで計算されるが、書き戻すとdoubleの場合でも64BITに丸め られる。なるべくメモリに書き戻さずにレジスタった方が高速なので、Release版 では、80BITで計算される「期間」が多くなる。そのため、Debug版とRelese版では 結果が僅かに違ってくる。 2. fpu control word が違っていて、st(0)〜st(7)に入っていても、計算が 80BITか、64BIT、32BITのどれで計算されるか異なったり、丸め方が四捨五入 か、正負二種類の方向の切り捨てなどが変わっている。 そして、IDEから起動した場合と、コマンドラインから起動した場合で結果が違う のは、「2.」の理由によるものではないかと。そして、実際に、インラインアセンブラ で fpu control word を読み取ってみると、不安定な値が読み出されたと。
221 名前:デフォルトの名無しさん [2018/09/16(日) 13:58:15.86 ID:haV9TZ8e.net] まず、 __asm{ fnstcw [cw]; } ではなく、_control87() を使ってみて欲しい。 インラインアセンブラは、独立した *.asm で書くより危険な場合が あるかも知れないので。特にC関数の引数、今の場合は、「cw」を インライン・アセンブラで用いた場合、正しいコードが出ているかどうか は注意が必要。
222 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 14:05:28.41 ID:haV9TZ8e.net] >>209 よく見ると、それは、かなり複雑な事情が絡みそうなコード。 以下のようにした方が安心。なお、「cw」という短すぎ
223 名前:驤数名 も長年のプログラミング経験からすると、インラインアセンブラでは 怖い。また、 TTTT reg,引数名 や TTTT 引数名 は大丈夫でも、 TTTT reg,[引数名] や TTTT [引数名] は1命令では不可能な事をコンパイラに指示している事になるので ちょっと怖い。間接の間接、つまり、[[ebp+8]]みたいな事を要求 しているが、そんなオペランドが使えるアセンブリ命令はx86/x64 では存在しないので。 #pragma unmanaged inline void fpu_getcw(unsigned short *pCW) { __asm{ mov edx,pCW fnstcw [edx]; } } #pragma managed [] [ここ壊れてます]
224 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 14:17:40.34 ID:haV9TZ8e.net] あ、後、インライン・アセンブラで実験する場合は、関数名の inline は 「取った」方がいい。つまり、以下の方が安心: #pragma unmanaged void fpu_getcw(unsigned short *pCW) { __asm{ mov edx,pCW fnstcw [edx]; } } #pragma managed
225 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 15:23:18.78 ID:h8nMbN0G.net] また基本に戻るが、>>193 で/MDになってるので /MTや/MTdでも発生するかしてみた方がいい
226 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 15:37:02.44 ID:Q5j4SiHR.net] .netはx87コンテキストをすべて保持しませんって分かったんだからもう十分。 win32かx64にすれば解決。そもそも問題になる仕様バグじゃない。
227 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 15:40:56.17 ID:haV9TZ8e.net] >>220 ホントだ。/MDだと、Runtime Library として DLL のものを使ってしまう。 これは、今回の現象に非常に重要な影響を与えているかも知れない。
228 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 15:42:36.82 ID:haV9TZ8e.net] >>221 いや、まだまだ興味深い。 ここで終わらせずにもっと徹底して追及すべきだ。
229 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 15:48:45.63 ID:zL1WUjLu.net] >>212 それはどうやらclrでは使えないらしい。 > These functions are ignored when you use /clr (Common Language Runtime Compilation) or /clr:pure to compile > because the common language runtime (CLR) only supports the default floating-point precision. > https://msdn.microsoft.com/en-us/library/e9b52ceh.aspx とはいえ無理矢理やってみた。警告は出るがコンパイルは通る。 結果は、どこに置いても、Debug/Releaseでも、常に 0x9001f が読み出される。 ただし、これは上記の仕様からして、当てにならない。
230 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 15:49:44.99 ID:zL1WUjLu.net] >>219 218のコードで試してみた結果、209で言った不安定さはなくなり、 全てにおいて 0x027f が安定して読み出せるようになった。 ただしその過程で気づいたが、 IDEから起動した場合はReleaseビルドであっても、「未初期化のスタック値」も0x00が読み出せるようだ。 どうやらこれが原因の可能性が出てきた。(はっきり言って俺のバグだが) コードは以下の通りだが、 unsigned short fpu_cw, fpu_cw_after; // fpu_getcw(&fpu_cw); double norm = calc_norm_and_regulate(count, inputs, false); fpu_getcw(&fpu_cw_after); Console::Write(String::Format("{0:D}, 0x{0:x4}\r\n",fpu_cw)); Console::Write(String::Format("{0:D}, 0x{0:x4}\r\n",fpu_cw_after)); 読み出しと書き出し(Console::Write)を両方ともコメントアウトするのが面倒なので、 色々試す際、読み出しだけコメントアウトし、不定を表示させて脳内で省略していたのだが、 IDEから起動した場合はReleaseビルドであっても必ず0x0000が表示される事に気づいた。 上記『初期化していない』 fpu_cw を Releaseビルドをコマンドプロンプトから実行: 不定 ReleaseビルドをIDEから実行: 常に0x0000 となる。 実行前にあらかじめスタック領域を0fillでもしているのか? まあこれに当たっているのなら確実に俺のバグだし、これなら辻褄は合ってしまうのだが。
231 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 15:51:01.50 ID:zL1WUjLu.net] >>219 なお、逆アセンブルでコードバイトを表示させて確かめることは出来る。 正しいコードは出ている。(ただし不安定) inline void fpu_getcw(unsigned short* cw) { 00DA1540 55 push ebp 00DA1541 8B EC mov ebp,esp __asm{ fnstcw [cw]; 00DA1543 D9 7D 08 fnstcw word ptr [cw] } } 00DA1546 5D pop ebp 00DA1547 C3 ret fnstcwは D9 /7 で 7D なら [EBP+disp8] となり、 7D 08 は [EBP+08] となる。 つまりスタックポインタ+8の領域に書き戻せ、となる。 [ebp+0]は元のebpが入っているから、(pushしているので) [ebp+4]にcallの戻り値アドレス [ebp+8]にcw(第一引数)が入っていることになる。 これは正しいコードだ。 しかし再度試したが、確かに不安定だ。何故かは分からん。 inline取ってみても不安定のまま。 > そんなオペランドが使えるアセンブリ命令はx86/x64 > では存在しないので。 正直、/7の意味が分からないのだが、説明は > /digit − 0 から7 までの数字で、命令のModR/M バイトがr/m(レジスタまたはメモリ)オペランドだけを使用することを示す。 > reg フィールドには、命令のオペコードを拡張する数字が入っている。(Intelのマニュアルより) となっているのだが、これはどういう意味だ? ModR/Mバイトが全部使えるとすると [ebp+disp8]出来ることになる。そしてそのコードは出ている。 ただし、動作は怪しいのも事実。 ModR/Mの一部しか使えない、ということか?
232 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 15:51:25.16 ID:zL1WUjLu.net] >>219 218のコードだと、 00381002 EC in al,dx __asm{ mov edx,pCW 00381003 8B 55 08 mov edx,dword ptr [pCW] fnstcw [edx]; 00381006 D9 3A fnstcw word ptr [edx] } } 00381008 5D pop ebp 00381009 C3 ret D9 3A ならまんま fnstcw [edx] だ。 理由は分からんがこちらだと安定しているので、結果としてはこのやり方が正しい。
233 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:02:47.64 ID:haV9TZ8e.net] >>226 をを。やはり、ある意味ではVCが間違ったアセンブリコードを出していたよ。 それだと、 fnstcw [EBP+08] という意味になってしまって、 fnstcw pCW の意味になっている。つまり: pCW = control_word; あなたが、やりたいのは、 *pCW = control_word; だったのだから、アセンブリ・コードが間違ってる。 あなたが指示したのは、 fnstcw [pCW] だった。実際に生成されたコードは、 fnstcw pCW だった。 VC のインラインアセンブラは、エラーも出さずに間違ったコードを 出すことが証明された。 これと、精度が不安定な問題とは全く別ではあるけれど。
234 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:06:05.39 ID:zL1WUjLu.net] すまん、間違いの修正 >>225 × > どうやらこれが原因の可能性が出てきた。(はっきり言って俺のバグだが) × > まあこれに当たっているのなら確実に俺のバグだし、これなら辻褄は合ってしまうのだが。 今回は俺はあくまで俺の本番コードのデバッグを念頭に置いていて、この発言だった。 ただし>>192 の再現コードで『不定スタック領域』を掴んでいるわけもなく、 一応IDE起動とコマンドプロンプト起動での挙動の違いを再現出来ているわけだから、 これだけが問題ではないのも事実だ。 俺にとっては一つ新しい知見として、 ・IDEから起動した場合、スタックが初期化されるっぽい ということが分かった。とはいえOSは0fillしてから各プロセスにメモリを与えるので、実際は、 ・コマンドプロンプト起動ならmain前に設定した続きでそのまま実行、 ・IDE起動ならmain前に色々やって0fillして実行、 或いはmain前に色々やることが多く、スタックが進み、(例えばデバッガをアタッチする為) 結果的にOSが初期化済みの領域から始動 となって違いが発生するというところか。
235 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:11:52.18 ID:haV9TZ8e.net] >>226 >正直、/7の意味が分からないのだが、 ModRM とは、 mod reg r/m 76 543 210 のようなオペランドを指しているのだけど、/7 は、regの部分を2進数の111、 10進数の「7」にするという意味。このタイプのマシン語は、 mod ttt r/m とも書かれる。tttの部分は、命令の主幹部分(ニモニック部分)によって変わる。 普通は、レジスタ番号を入れるところに、命令の種類を表す3BITの値を入れる 仕様になっている。 あなたがインラインアセンブラでVCに出させたかったコードは、意味的には、 fnstcw [[EBP+08]] なのだが、[ ] を二重にしたようなそんなx86/x64命令は存在しないので VC がエラーも出さずに勝手に一重の fnstcw [EBP+08] にしてしまった、という事。本当は、 mov edx,[EBP+08] fnstcw [edx]
236 名前: というコードにしなくてはならなかったのに、VCがある意味では間違った。 これが、単独の *.asm ではなく、VC の asm {・・・} が危険な理由。 VC の asm は特に危険。 [] [ここ壊れてます]
237 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:17:02.07 ID:haV9TZ8e.net] >>229 >ただし>>192 の再現コードで『不定スタック領域』を掴んでいるわけもなく、 >一応IDE起動とコマンドプロンプト起動での挙動の違いを再現出来ているわけだから、 >これだけが問題ではないのも事実だ。 そうだよ。精度が変わるのはあなたの間違いではない。スタック領域が0クリア されようがれまいが、あなたのコード自体には特に不安定さはない。 非初期化領域を参照しているコードは見当たらないし。
238 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:20:22.27 ID:haV9TZ8e.net] 逆アセンブラ結果を見てないで言うけど、もし、sqrt() が call文で関数呼び出し されているんだったら、そこで精度の違いが出てるかもしれない。
239 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:23:20.75 ID:zL1WUjLu.net] >>228 なるほど、了解した。 つまり、>>210 は全面的に間違いで、正しくは、 ・fpu control register は 0x027F で、IDEからも正しく読めている だな。 俺がやるべきだったのは fnstcw [[cw]] なのだと思うが、これはSyntaxErrorだ。 そして、こんな命令はないから、 []内に変数を書かず、レジスタ名にしろ、ということだったのだな。 全くもって了解だ。 VCの問題ではなくて、 俺が fnstcw [cw] と書いたのが間違いで、それをそのままコードにされてしまっただけだな。 正しく書けばSyntaxErrorだったのだし。 なお fnstcw [*cw] もSyntaxErrorだ。手動で一旦レジスタに移さないと駄目だな。 全くもって>>219 のコードが正しい。
240 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:35:08.59 ID:LrdaMWHl.net] >>233 >俺がやるべきだったのは fnstcw [[cw]] なのだと思うが、これはSyntaxErrorだ。 ちょっと違う。あなたはやるべきことをちゃんと正しく、 fnstcw [cw] と書いた。しかし、cw=[ebp+8]なので、これは、 fnstcw [[ebp+8]] という「意味」になる。でも、x86/x64のマシン語にはこんな[ ]を二重にした オペランドは存在しないので、VCが無断で勝手に[ ]を一重にして、 fnstcw [ebp+8] に改変してしまった。 **(ebp+8) = control_word; としなくてはならないのに、VCが勝手に、 *(ebp+8) = control_word; としたということ。
241 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:36:04.13 ID:zL1WUjLu.net] >>230-231 了解だ。ありがとう。 >>232 その部分の逆アセンブラは以下の通り。 普通にcallされている。(行数オーバーなので切るが) ただし、 > そこで精度の違いが出てるかもしれない との繋がりがよくからない。 sqrt()でcallされると、スタックが改変される。おそらくデータ依存か? なら未初期化のスタックを掴みに行っているコードが有ればバグる。 ただし今回の『再現コード』はこの限りではない。 (俺の本番コードはさておき)
242 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:37:19.30 ID:zL1WUjLu.net] >>232 逆アセンブラ for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i]; 00000033 33 D2 xor edx,edx 00000035 89 55 E8 mov dword ptr [ebp-18h],edx 00000038 90 nop 00000039 EB 03 jmp 0000003E 0000003b FF 45 E8 inc dword ptr [ebp-18h] 0000003e 8B 45 E8 mov eax,dword ptr [ebp-18h] 00000041 3B 45 FC cmp eax,dword ptr [ebp-4] 00000044 7D 1B jge 00000061 00000046 8B 45 F8 mov eax,dword ptr [ebp-8] 00000049 8B 55 E8 mov edx,dword ptr [ebp-18h] 0000004c DD 04 D0 fld qword ptr [eax+edx*8] 0000004f 8B 45 F8 mov eax,dword ptr [ebp-8] 00000052 8B 55 E8 mov edx,dword ptr [ebp-18h] 00000055 DC 0C D0 fmul qword ptr [eax+edx*8] 00000058 DC 45 F0 fadd qword ptr [ebp-10h] 0000005b DD 5D F0 fstp qword ptr [ebp-10h] 0000005e 90 nop 0000005f EB DA jmp 0000003B norm = sqrt(norm); 00000061 DD 45 F0 fld qword ptr [ebp-10h] 00000064 83 EC 08 sub esp,8 00000067 DD 1C 24 fstp qword ptr [esp] 0000006a E8 0D 50 7B FF call FF7B507C 0000006f DD 5D D8 fstp qword ptr [ebp-28h] 00000072 DD 45 D8 fld qword ptr [ebp-28h] 00000075 DD 5D F0 fstp qword ptr [ebp-10h]
243 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:37:34.94 ID:zL1WUjLu.net] >>232 逆アセンブラ(続き) if (regulate) for (int i=0;i<num;i++) r[i] = (T)(r[i]/norm); 00000078 0F B6 45 08 movzx eax,byte ptr [ebp+8] 0000007c 85 C0 test eax,eax 0000007e 74 25 je 000000A5 00000080 33 D2 xor edx,edx 00000082 89 55 EC mov dword ptr [ebp-14h],edx 00000085 90 nop 00000086 EB 03 jmp 0000008B 00000088 FF 45 EC inc dword ptr [ebp-14h] 0000008b 8B 45 EC mov eax,dword ptr [ebp-14h] 0000008e 3B 45 FC cmp eax,dword ptr [ebp-4] 00000091 7D 12 jge 000000A5 00000093 8B 45 F8 mov eax,dword ptr [ebp-8] 00000096 8B 55 EC mov edx,dword ptr [ebp-14h] 00000099 DD 45 F0 fld qword ptr [ebp-10h] 0000009c DC 3C D0 fdivr qword ptr [eax+edx*8] 0000009f DD 1C D0 fstp qword ptr [eax+edx*8] 000000a2 90 nop 000000a3 EB E3 jmp 00000088 return norm; 000000a5 DD 45 F0 fld qword ptr [ebp-10h] 000000a8 DD 5D E0 fstp qword ptr [ebp-20h]
244 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:40:36.14 ID:LrdaMWHl.net] >>235 よく見ると、最小(?)の実験コードでは sqrt() が使われていなかった。 スマン。
245 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:42:54.99 ID:zL1WUjLu.net] >>234 ああ、なるほど、了解。
246 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:49:54.65 ID:zL1WUjLu.net] >>238 いや、俺が提供した>>192 のソースなら使われてるぞ。 >>201 のソースでは使われてないが。 ただまあ、彼(200)がsqrtを落としたのも分からなくはない。 誤差が生じる=通常は桁落ちだから、この場合は当然積和部分が怪しい。 あらかじめ彼はそうなると分かっていてそれを落とし、予定調和的な結論にたどり着いてしまった。 それが彼の間違いだった、ということ。 俺は出来るだけ元のソースのままで追跡しようとしている。 元のソースの該当ケースと離れてしまっては意味がないから。 そして元ソースではsqrtを使っている。
247 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:53:58.12 ID:/oSJzlqn.net] たぶん2008の最適化ミスだと思う。 static double norm = 0;// ←"static"を追加する にするとか、最適化オプションをいじると Release/コマンドプロンプトからの起動でも 0x1ff68ddfb62221ddになる
248 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:54:10.71 ID:LrdaMWHl.net] >>238 ああ。また訂正。 sqrt()が使われていないのは、>>201 , >>202 , >>203 , >>204 の場合で、 それは、ループ内にfprintf()を入れた場合と入れない場合とで、 x87 fpuレジスタのst(0)〜st(7)を使う「期間」が変わるために 80BITから 64BITへの書き戻し丸めの問題のために精度が変わっているだけだった。 一方、あなたが指摘した >>192 では、ちゃんと sqrt() 関数が使われていて、 それだと、IDEからの起動とコマンド・プロンプトからの起動とで、精度が変 わってくると。そして、その場合の逆アセンブル結果は >>236 のように sqrt() 関数がその場で x87 fpu の fsqrt 命令を使わずに、call 文によって 実際に本当のサブ・ルーチンを呼び出していると。 これはとても興味深い。そのサブ・ルーチンの中が、時と場合によって 精度が変わってくるような書き方をされている可能性が見えてきた。
249 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 16:56:42.67 ID:LrdaMWHl.net] >>240 >いや、俺が提供した>>192 のソースなら使われてるぞ。 > >>201 のソースでは使われてないが。 了解。 問題を切り分けるため、sqrt() を使わなかった場合の Release版での、 IDE起動とコマンドrライン起動の精度の違いを実験してみて欲しい。
250 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 17:02:35.56 ID:LrdaMWHl.net] ちょっとしばらく、ここを離れる。
251 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 17:22:07.21 ID:zL1WUjLu.net] >>241 現象確認した。こちらでも再現した。 逆アセンブルは、以下。(肝心のループ部分は次レス内) 正直、fld/fmul/fadd/fstpのループ部分は変わらず、 normのアドレスが [ebp-10h](つまりローカル)から ds:[00A4AD40h](つまりグローバル)に変わっただけであり、 これで結果が変わるのはかなり奇妙な気もするが、何か見落としがあるのかも。 >>241 逆アセンブル(static付加版) template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug. static double norm = 0; for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i]; 00000000 55 push ebp 00000001 8B EC mov ebp,esp 00000003 83 EC 20 sub esp,20h 00000006 89 4D FC mov dword ptr [ebp-4],ecx 00000009 89 55 F8 mov dword ptr [ebp-8],edx 0000000c 83 3D 14 2E 38 00 00 cmp dword ptr ds:[00382E14h],0 00000013 74 05 je 0000001A 00000015 E8 FF 52 30 68 call 68305319 0000001a 33 D2 xor edx,edx 0000001c 89 55 F0 mov dword ptr [ebp-10h],edx 0000001f 33 D2 xor edx,edx 00000021 89 55 F4 mov dword ptr [ebp-0Ch],edx 00000024 D9 EE fldz 00000026 DD 5D E8 fstp qword ptr [ebp-18h] 00000029 33 D2 xor edx,edx 0000002b 89 55 F0 mov dword ptr [ebp-10h],edx 0000002e 90 nop 0000002f EB 03 jmp 00000034
252 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 17:22:29.32 ID:zL1WUjLu.net] >>241 逆アセンブル(続き)(static付加版) 00000031 FF 45 F0 inc dword ptr [ebp-10h] 00000034 8B 45 F0 mov eax,dword ptr [ebp-10h] 00000037 3B 45 FC cmp eax,dword ptr [ebp-4] 0000003a 7D 21 jge 0000005D 0000003c 8B 45 F8 mov eax,dword ptr [ebp-8] 0000003f 8B 55 F0 mov edx,dword ptr [ebp-10h] 00000042 DD 04 D0 fld qword ptr [eax+edx*8] 00000045 8B 45 F8 mov eax,dword ptr [ebp-8] 00000048 8B 55 F0 mov edx,dword ptr [ebp-10h] 0000004b DC 0C D0 fmul qword ptr [eax+edx*8] 0000004e DC 05 40 AD A4 00 fadd qword ptr ds:[00A4AD40h] 00000054 DD 1D 40 AD A4 00 fstp qword ptr ds:[00A4AD40h] 0000005a 90 nop 0000005b EB D4 jmp 00000031 norm = sqrt(norm); 0000005d DD 05 40 AD A4 00 fld qword ptr ds:[00A4AD40h] 00000063 83 EC 08 sub esp,8 00000066 DD 1C 24 fstp qword ptr [esp] 00000069 E8 0E 50 88 FF call FF88507C 0000006e DD 5D E0 fstp qword ptr [ebp-20h] 00000071 DD 45 E0 fld qword ptr [ebp-20h] 00000074 DD 1D 40 AD A4 00 fstp qword ptr ds:[00A4AD40h]
253 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 17:22:46.06 ID:zL1WUjLu.net] >>241 逆アセンブル(続き)(static付加版) if (regulate) for (int i=0;i<num;i++) r[i] = (T)(r[i]/norm); 0000007a 0F B6 45 08 movzx eax,byte ptr [ebp+8] 0000007e 85 C0 test eax,eax 00000080 74 28 je 000000AA 00000082 33 D2 xor edx,edx 00000084 89 55 F4 mov dword ptr [ebp-0Ch],edx 00000087 90 nop 00000088 EB 03 jmp 0000008D 0000008a FF 45 F4 inc dword ptr [ebp-0Ch] 0000008d 8B 45 F4 mov eax,dword ptr [ebp-0Ch] 00000090 3B 45 FC cmp eax,dword ptr [ebp-4] 00000093 7D 15 jge 000000AA 00000095 8B 45 F8 mov eax,dword ptr [ebp-8] 00000098 8B 55 F4 mov edx,dword ptr [ebp-0Ch] 0000009b DD 05 40 AD A4 00 fld qword ptr ds:[00A4AD40h] 000000a1 DC 3C D0 fdivr qword ptr [eax+edx*8] 000000a4 DD 1C D0 fstp qword ptr [eax+edx*8] 000000a7 90 nop 000000a8 EB E0 jmp 0000008A return norm; 000000aa DD 05 40 AD A4 00 fld qword ptr ds:[00A4AD40h] 000000b0 DD 5D E8 fstp qword ptr [ebp-18h] } 000000b3 DD 45 E8 fld qword ptr [ebp-18h] 000000b6 8B E5 mov esp,ebp 000000b8 5D pop ebp 000000b9 C2 04 00 ret 4
254 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 17:35:35.70 ID:zL1WUjLu.net] >>243 まだ異なった出力が得られた。 この意味では200がsqrtを外した判断は正しかった。 (彼はそこからさらにループ回数を固定してしまったのが間違いだった) 191ソースを以下に変更した。(sqrtをコメントアウト) ついでに Console::Write(String::Format("{0:E6}, {0:E30}\r\n",norm)); の出力も付けておく。 ソース: template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug. double norm = 0; for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i]; // norm = sqrt(norm); if (regulate) for (int i=0;i<num;i++) r[i] = (T)(r[i]/norm); return norm; } 結果:(Releaseビルド/コマンドプロンプトからの起動) 0.000000, 0x0007f2c44dfff8f2 1.105348E-308, 1.105348254058510600000000000000E-308 結果:(Releaseビルド/IDEからの起動、Debugビルドは起動方法によらずこちら) 0.000000, 0x0007f2c44dfff8f1 1.105348E-308, 1.105348254058510100000000000000E-308 >>244 了解。いずれにしても助かってる。 こちらも後30分くらいでちょっと離れる予定。
255 名前:デフォルトの名無しさん [2018/09/16(日) 18:30:34.19 ID:HF0YmRsW.net] >>213 ほんそれ
256 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 20:54:27.39 ID:zL1WUjLu.net] >>241 さて再見したが、やはりstaticだけで直る理由は分からない。 なお、最適化ミスの場合は、逆アセンブラを読めば分かる。 今のところそれではない。 一応、>>192 ソースのtemplate部の逆アセンブルを上げておく。(ただし重複するので頭のみ) 頭はこれ。続きが>>236 ,237。 template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug. double norm = 0; 00000000 55 push ebp 00000001 8B EC mov ebp,esp 00000003 83 EC 28 sub esp,28h 00000006 89 4D FC mov dword ptr [ebp-4],ecx 00000009 89 55 F8 mov dword ptr [ebp-8],edx 0000000c 83 3D 14 2E 76 00 00 cmp dword ptr ds:[00762E14h],0 00000013 74 05 je 0000001A 00000015 E8 FF 52 1B 68 call 681B5319 0000001a 33 D2 xor edx,edx 0000001c 89 55 E8 mov dword ptr [ebp-18h],edx 0000001f 33 D2 xor edx,edx 00000021 89 55 EC mov dword ptr [ebp-14h],edx 00000024 D9 EE fldz 00000026 DD 5D F0 fstp qword ptr [ebp-10h] 00000029 D9 EE fldz 0000002b DD 5D E0 fstp qword ptr [ebp-20h] 0000002e D9 EE fldz 00000030 DD 5D F0 fstp qword ptr [ebp-10h]
257 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 21:25:23.90 ID:zL1WUjLu.net] >>220 >>222 /MTと/clrは同時に指定出来ないらしい。(error D8016) /MTdも同じく無理。 もう一つ /MDd ってのがあるから試してみた。 /MDdの結果: Releaseビルドでコマンドプロンプト起動の時のみ ****de、 ReleaseビルドでIDEからの起動だと ***dd。(Debugビルドは起動方法を問わずこっち) (/MDと全く挙動は同じ) これで有効な指摘については全て回答してるかな? 見落としが有れば指摘よろしく。 (規制に引っかかったので遅くなってすまん) 今のところ、可能性があるのは以下か? ・Releaseビルドをコマンドプロンプトから起動したときのみなぜか精度が高い (>>201 から結果的に検出された。今のところ精度が高いときと同じ挙動をしている為) ・ReleaseビルドもIDEから起動すれば結果的にスタックが0初期化されている状態になっており、 俺の本番プログラムに関してはここに当たるバグがある?(>>229 ) (ただしこれは>>192 には該当しない)
258 名前:240 mailto:sage [2018/09/16(日) 21:43:04.47 ID:/oSJzlqn.net] .netの場合、デバッガ配下では(デバッグのため)違うコードを実行しているような気がする。 デバッガの逆アセンブル表示とかasm出力はあまり当てにならないような気もする。 ループ部分だけど、レジスタのみで処理するか、メモリを使用するかで精度が変わるのかも。 そもそも、どっちが正しいのかよくわからんけど... ループ部分の関数を#pragma unmanagedすると結果が変わるでそれが正しいのかも。
259 名前:240 mailto:sage [2018/09/16(日) 21:43:27.78 ID:/oSJzlqn.net] static版 0000000e 33 C0 xor eax,eax 00000010 85 F6 test esi,esi 00000012 7E 16 jle 0000002A 00000014 DD 04 C7 fld qword ptr [edi+eax*8] 00000017 DC C8 fmul st(0),st 00000019 DC 05 00 30 CC 00 fadd qword ptr ds:[00CC3000h] 0000001f DD 1D 00 30 CC 00 fstp qword ptr ds:[00CC3000h] 00000025 40 inc eax 00000026 3B C6 cmp eax,esi 00000028 7C EA jl 00000014 0000002a DD 05 00 30 CC 00 fld qword ptr ds:[00CC3000h] 非static版 0000000e D9 EE fldz 00000010 33 C0 xor eax,eax 00000012 85 F6 test esi,esi 00000014 7E 0C jle 00000022 00000016 DD 04 C7 fld qword ptr [edi+eax*8] 00000019 DC C8 fmul st(0),st 0000001b DE C1 faddp st(1),st 0000001d 40 inc eax 0000001e 3B C6 cmp eax,esi 00000020 7C F4 jl 00000016
260 名前:デフォルトの名無しさん mailto:sage [2018/09/16(日) 22:27:34.58 ID:zL1WUjLu.net] >>252 とりあえず落ち着け。一つずつ行こう。 > ループ部分の関数を#pragma unmanagedすると結果が変わるでそれが正しいのかも。 こちらでも確認した。 calc_norm_and_regulateをunmanaged関数にすると、違いはなくなる。 (Releaseビルドの`をコマンドプロンプトで起動した際にも、****ddの結果となる) ただしこちらの逆アセンブル結果は以下だ。(fld/fmul/fadd/fstpであることに注意) for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i]; 0007272C C7 45 F4 00 00 00 00 mov dword ptr [i],0 00072733 EB 09 jmp `anonymous namespace'::calc_norm_and_regulate<double>+1Eh (7273Eh) 00072735 8B 45 F4 mov eax,dword ptr [i] 00072738 83 C0 01 add eax,1 0007273B 89 45 F4 mov dword ptr [i],eax 0007273E 8B 4D F4 mov ec
261 名前:x,dword ptr [i] 00072741 3B 4D 08 cmp ecx,dword ptr [num] 00072744 7D 1A jge `anonymous namespace'::calc_norm_and_regulate<double>+40h (72760h) 00072746 8B 55 F4 mov edx,dword ptr [i] 00072749 8B 45 0C mov eax,dword ptr [r] 0007274C 8B 4D F4 mov ecx,dword ptr [i] 0007274F 8B 75 0C mov esi,dword ptr [r] 00072752 DD 04 D0 fld qword ptr [eax+edx*8] 00072755 DC 0C CE fmul qword ptr [esi+ecx*8] 00072758 DC 45 F8 fadd qword ptr [norm] 0007275B DD 5D F8 fstp qword ptr [norm] 0007275E EB D5 jmp `anonymous namespace'::calc_norm_and_regulate<double>+15h (72735h) [] [ここ壊れてます]