[過去ログ] Visual Studio 2008 Part 22 (314レス)
前次1-
抽出解除 必死チェッカー(本家) (べ) 自ID レス栞 あぼーん

このスレッドは過去ログ倉庫に格納されています。
次スレ検索 歴削→次スレ 栞削→次スレ 過去ログメニュー
207: 2018/09/16(日)08:28 ID:zL1WUjLu(1/27) AAS
>>198以降、
すまん、入れ替わりになるかもしれんが後で確認する。
まず>>194その他について回答する。

>>194
SSEは /arch:SSE または /arch:SSE2 でないと出ないことになっており、勿論設定はしていない。
また、逆アセンブル結果では x87 命令のみであるのも確認している。
ただ今回の問題は、本当にReleaseビルドのバイナリを逆アセンブルしているか怪しい事だが。

>>195
小さい値なのは偶々だ。
辿って行ってそれが1回目にヒットする入力データだっただけのこと。

>>196
.NETの書式指定はググり難いが以下。
外部リンク:docs.microsoft.com
概ね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)

ただ当たり前だが、書式を変えたところで計算結果は変わらないし、
精度の問題には関係ない。
208
(1): 2018/09/16(日)08:29 ID:zL1WUjLu(2/27) AAS
>>194
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方向への切り捨て
209
(3): 2018/09/16(日)08:29 ID:zL1WUjLu(3/27) AAS
>>194
直後のみに配置:
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(コマンドプロンプト)

以上が>>194その他に対する回答。
これから>>198その他について確認する。
213
(1): 2018/09/16(日)13:33 ID:zL1WUjLu(4/27) AAS
>>198
再現実験ありがとう。
しかし色々問題がある。

1. 俺は起動方法による違いについてフォーカスしているが、
 君はRelease/Debugの違いにフォーカスしている。
2. VC++2008では再現しない。(VC++2010では再現する)
3. ソース改変しすぎ。それでは意味がない。
4. >>206の結論は間違い。

まず問題なのはソースの改変だ。
ループ回数を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と同じコードだからだ。
勿論結果も同じだった。
214: 2018/09/16(日)13:33 ID:zL1WUjLu(5/27) AAS
>>198
問題は、俺の環境で俺が提供したコード>>191だと、
同様に展開されないにも関わらず、『起動方法によって』結果が異なってしまう点だ。
俺の環境での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関連のことはこちらも知っている上で、質問している。
223: 2018/09/16(日)15:48 ID:zL1WUjLu(6/27) AAS
>>211
それはどうやら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.
> 外部リンク[aspx]:msdn.microsoft.com

とはいえ無理矢理やってみた。警告は出るがコンパイルは通る。
結果は、どこに置いても、Debug/Releaseでも、常に 0x9001f が読み出される。
ただし、これは上記の仕様からして、当てにならない。
224
(1): 2018/09/16(日)15:49 ID:zL1WUjLu(7/27) AAS
>>218
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でもしているのか?
まあこれに当たっているのなら確実に俺のバグだし、これなら辻褄は合ってしまうのだが。
225
(2): 2018/09/16(日)15:51 ID:zL1WUjLu(8/27) AAS
>>218
なお、逆アセンブルでコードバイトを表示させて確かめることは出来る。
正しいコードは出ている。(ただし不安定)
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の一部しか使えない、ということか?
226: 2018/09/16(日)15:51 ID:zL1WUjLu(9/27) AAS
>>218
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] だ。
理由は分からんがこちらだと安定しているので、結果としてはこのやり方が正しい。
228
(2): 2018/09/16(日)16:06 ID:zL1WUjLu(10/27) AAS
すまん、間違いの修正

>>224
× > どうやらこれが原因の可能性が出てきた。(はっきり言って俺のバグだが)
× > まあこれに当たっているのなら確実に俺のバグだし、これなら辻褄は合ってしまうのだが。

今回は俺はあくまで俺の本番コードのデバッグを念頭に置いていて、この発言だった。
ただし>>191の再現コードで『不定スタック領域』を掴んでいるわけもなく、
一応IDE起動とコマンドプロンプト起動での挙動の違いを再現出来ているわけだから、
これだけが問題ではないのも事実だ。

俺にとっては一つ新しい知見として、
・IDEから起動した場合、スタックが初期化されるっぽい
ということが分かった。とはいえOSは0fillしてから各プロセスにメモリを与えるので、実際は、
・コマンドプロンプト起動ならmain前に設定した続きでそのまま実行、
・IDE起動ならmain前に色々やって0fillして実行、
 或いはmain前に色々やることが多く、スタックが進み、(例えばデバッガをアタッチする為)
 結果的にOSが初期化済みの領域から始動
となって違いが発生するというところか。
232
(1): 2018/09/16(日)16:23 ID:zL1WUjLu(11/27) AAS
>>227
なるほど、了解した。
つまり、>>209は全面的に間違いで、正しくは、

・fpu control register は 0x027F で、IDEからも正しく読めている

だな。

俺がやるべきだったのは fnstcw [[cw]] なのだと思うが、これはSyntaxErrorだ。
そして、こんな命令はないから、
[]内に変数を書かず、レジスタ名にしろ、ということだったのだな。
全くもって了解だ。

VCの問題ではなくて、
俺が fnstcw [cw] と書いたのが間違いで、それをそのままコードにされてしまっただけだな。
正しく書けばSyntaxErrorだったのだし。
なお fnstcw [*cw] もSyntaxErrorだ。手動で一旦レジスタに移さないと駄目だな。
全くもって>>218のコードが正しい。
234
(1): 2018/09/16(日)16:36 ID:zL1WUjLu(12/27) AAS
>>229-230
了解だ。ありがとう。

>>231
その部分の逆アセンブラは以下の通り。
普通にcallされている。(行数オーバーなので切るが)

ただし、
> そこで精度の違いが出てるかもしれない
との繋がりがよくからない。
sqrt()でcallされると、スタックが改変される。おそらくデータ依存か?
なら未初期化のスタックを掴みに行っているコードが有ればバグる。
ただし今回の『再現コード』はこの限りではない。
(俺の本番コードはさておき)
235
(4): 2018/09/16(日)16:37 ID:zL1WUjLu(13/27) AAS
>>231
逆アセンブラ

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]
236
(1): 2018/09/16(日)16:37 ID:zL1WUjLu(14/27) AAS
>>231
逆アセンブラ(続き)

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]
238: 2018/09/16(日)16:42 ID:zL1WUjLu(15/27) AAS
>>233
ああ、なるほど、了解。
239
(1): 2018/09/16(日)16:49 ID:zL1WUjLu(16/27) AAS
>>237
いや、俺が提供した>>191のソースなら使われてるぞ。
>>200のソースでは使われてないが。

ただまあ、彼(200)がsqrtを落としたのも分からなくはない。
誤差が生じる=通常は桁落ちだから、この場合は当然積和部分が怪しい。
あらかじめ彼はそうなると分かっていてそれを落とし、予定調和的な結論にたどり着いてしまった。
それが彼の間違いだった、ということ。

俺は出来るだけ元のソースのままで追跡しようとしている。
元のソースの該当ケースと離れてしまっては意味がないから。
そして元ソースではsqrtを使っている。
244: 2018/09/16(日)17:22 ID:zL1WUjLu(17/27) AAS
>>240
現象確認した。こちらでも再現した。
逆アセンブルは、以下。(肝心のループ部分は次レス内)

正直、fld/fmul/fadd/fstpのループ部分は変わらず、
normのアドレスが [ebp-10h](つまりローカル)から
ds:[00A4AD40h](つまりグローバル)に変わっただけであり、
これで結果が変わるのはかなり奇妙な気もするが、何か見落としがあるのかも。

>>240逆アセンブル(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
245: 2018/09/16(日)17:22 ID:zL1WUjLu(18/27) AAS
>>240逆アセンブル(続き)(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]
246: 2018/09/16(日)17:22 ID:zL1WUjLu(19/27) AAS
>>240逆アセンブル(続き)(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
247
(1): 2018/09/16(日)17:35 ID:zL1WUjLu(20/27) AAS
>>242
まだ異なった出力が得られた。
この意味では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

>>243
了解。いずれにしても助かってる。
こちらも後30分くらいでちょっと離れる予定。
249: 2018/09/16(日)20:54 ID:zL1WUjLu(21/27) AAS
>>240
さて再見したが、やはりstaticだけで直る理由は分からない。
なお、最適化ミスの場合は、逆アセンブラを読めば分かる。
今のところそれではない。

一応、>>191ソースのtemplate部の逆アセンブルを上げておく。(ただし重複するので頭のみ)
頭はこれ。続きが>>235,236

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]
250: 2018/09/16(日)21:25 ID:zL1WUjLu(22/27) AAS
>>219
>>221
/MTと/clrは同時に指定出来ないらしい。(error D8016)
/MTdも同じく無理。

もう一つ /MDd ってのがあるから試してみた。

/MDdの結果:
Releaseビルドでコマンドプロンプト起動の時のみ ****de、
ReleaseビルドでIDEからの起動だと ***dd。(Debugビルドは起動方法を問わずこっち)
(/MDと全く挙動は同じ)

これで有効な指摘については全て回答してるかな?
見落としが有れば指摘よろしく。
(規制に引っかかったので遅くなってすまん)

今のところ、可能性があるのは以下か?

・Releaseビルドをコマンドプロンプトから起動したときのみなぜか精度が高い
 (>>200から結果的に検出された。今のところ精度が高いときと同じ挙動をしている為)
・ReleaseビルドもIDEから起動すれば結果的にスタックが0初期化されている状態になっており、
 俺の本番プログラムに関してはここに当たるバグがある?(>>228)
 (ただしこれは>>191には該当しない)
253
(1): 2018/09/16(日)22:27 ID:zL1WUjLu(23/27) AAS
>>251
とりあえず落ち着け。一つずつ行こう。

> ループ部分の関数を#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 ecx,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)
254: 2018/09/16(日)22:33 ID:zL1WUjLu(24/27) AAS
>>252
そちらの逆アセンブルは以下の違いが出てるだろ。
static版: fld/fmul/fadd/fstp
非static版: fld/fmul/faddp (fstpが無い)
この非static版の場合、拡張倍精度(80bit)で演算されるから精度が高いことになり、
static版との演算結果に違いが出るのも仕様通りなんだよ。(これは>>200と同じ間違い)

一応、fstpにも80bit版はあって、Intelのマニュアルによると以下。
> オペコード命令説明
> D9 /2 FST m32fp ST(0) をm32fp にコピーする。
> DD /2 FST m64fp ST(0) をm64fp にコピーする。
> DD D0+i FST ST(i) ST(0) をST(i) にコピーする。
> D9 /3 FSTP m32fp ST(0) をm32fp にコピーし、レジスタスタックをポップする。
> DD /3 FSTP m64fp ST(0) をm64fp にコピーし、レジスタスタックをポップする。
> DB /7 FSTP m80fp ST(0) をm80fp にコピーし、レジスタスタックをポップする。
> DD D8+i FSTP ST(i) ST(0) をST(i) にコピーし、レジスタスタックをポップする。
つまり君のstatic版
> 0000001f DD 1D 00 30 CC 00 fstp qword ptr ds:[00CC3000h]
では FSTP /3 m64fp [disp32] であり、そこで64bit(倍精度)に丸められてる。
だからレジスタ(80bit=拡張倍精度)で演算される非static版と結果が異なる。
static版のsftpが DB /7 m80fp なら誤差は出ないはずなんだよ。(Cでどう書くのかは知らん)

だから>>252の場合の誤差なら、仕様通りなんだよ。(片方が倍精度、もう片方は拡張倍精度)
ただし、>>191は逆アセンブル(>>235)を見る限りそれに該当しないし、(両方とも倍精度)
今回の俺の上記逆アセンブル(>>253、中身は君の指摘通りunmanagedにしただけ)も該当しない。(両方とも倍精度)
そして253は何故か直ってしまった。
255: 2018/09/16(日)22:34 ID:zL1WUjLu(25/27) AAS
>>252
> .netの場合、デバッガ配下では(デバッグのため)違うコードを実行しているような気がする。
> デバッガの逆アセンブル表示とかasm出力はあまり当てにならないような気もする。
これは俺も相当疑っているのだが、今のところ尻尾を掴めない。
ILspyだっけ?外部の逆アセンブルツール使えばチェック出来るのかな?

いずれにしても、>>251の指摘
・unmanagedにすれば直る
のも事実だし、逆アセンブルを見る限り、これを説明出来る理由もないのも事実。
257
(1): 2018/09/16(日)23:24 ID:zL1WUjLu(26/27) AAS
>>256
> 252はRelease版をコンソールで実行したときの逆アセンブル結果。
それはどうやって得たの?俺はそれが出来ないから困ってる。

> ちなみに、235はDebugモードでコンパイルし、デバッガ配下の逆アセンブル結果でしょ。
235は、IDE上でReleaseモードでF5で起動し、ブレークポイントを当てて止めて逆アセンブルした結果。
俺が貼ってる逆アセンブル結果は全てこの方法で、IDEで表示されているもの。
だからIDEの表示がおかしかったら話が全部おかしくなる。

君がIDEから独立して逆アセンブル出来ているのなら、その方法を知りたい。
こちらでも試す。

なおILSpy、グダグダ言わずに試してみたが、
当たり前だがmanaged code だとILが出る(x86ではない)ので、
俺って根本的に間違ってたかも?
今までx86のアセンブラで議論してたけど、これって .NET アプリには同梱されていないというオチ?
(まあその場合は君がやっている外部逆アセンブルが単純には出来ないはずなのだが)
265
(1): 2018/09/16(日)23:58 ID:zL1WUjLu(27/27) AAS
>>258
おお、そのやり方は知らなかった。大変助かった。ありがとう。
で、結果だが、>>252とは微妙に違うが、確かに拡張倍精度で計算されている。
逆アセンブル結果は、以下。

0000000e D9 EE fldz
for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i];
00000010 33 C9 xor ecx,ecx
00000012 EB 01 jmp 00000015
00000014 41 inc ecx
00000015 3B CE cmp ecx,esi
00000017 7D 0B jge 00000024
00000019 DD 04 CF fld qword ptr [edi+ecx*8]
0000001c D9 C0 fld st(0)
0000001e DE C9 fmulp st(1),st
00000020 DE C1 faddp st(1),st
00000022 EB F0 jmp 00000014
norm = sqrt(norm);
00000024 83 EC 08 sub esp,8
00000027 DD 1C 24 fstp qword ptr [esp]
0000002a E8 49 7C F2 FF call FFF27C78

とにかく、Releaseビルドをコンソールから起動した場合は拡張倍精度になってるのは分かった。
なら、ReleaseビルドをIDEから起動した場合は何を起動してるんだこれは?
Debugビルドとも微妙にアドレス等が違うんだが。

とはいえ、これは「そもそも色々間違っている」可能性が出てきたので、もう一度全体を見直す。
明日(だけで済むとも思えないが)確認し、整理してまた投稿する。

とにかくありがとう。これはだいぶインパクトがある。(はず)
前次1-
スレ情報 赤レス抽出 画像レス抽出 歴の未読スレ AAサムネイル

ぬこの手 ぬこTOP 0.026s