[過去ログ] Visual Studio 2008 Part 22 (314レス)
前次1-
抽出解除 レス栞

このスレッドは過去ログ倉庫に格納されています。
次スレ検索 歴削→次スレ 栞削→次スレ 過去ログメニュー
191
(14): 2018/09/15(土)20:37 ID:UR1d6CKz(3/5) AAS
ソース:
#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;
省17
195
(1): 2018/09/16(日)03:19 ID:wIV2HUNW(2/4) AAS
>>191
// Release build
// 0.000000, 0x1ff68ddfb62221dd from IDE
// 0.000000, 0x1ff68ddfb62221de from command prompt

それにしても、随分小さな値だね。ちなみに、浮動小数点表示
の場合の有効数字の桁数を上げたら、どのようになる?
1.xxxe-yy
表示にして。
196
(2): 2018/09/16(日)03:40 ID:wIV2HUNW(3/4) AAS
>>191
試しに、ソースの冒頭に
#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) );

としてみるとどうなる?
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
省21
228
(2): 2018/09/16(日)16:06 ID:zL1WUjLu(10/27) AAS
すまん、間違いの修正

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

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

俺にとっては一つ新しい知見として、
・IDEから起動した場合、スタックが初期化されるっぽい
省6
230
(1): 2018/09/16(日)16:17 ID:haV9TZ8e(11/12) AAS
>>228
>ただし>>191の再現コードで『不定スタック領域』を掴んでいるわけもなく、
>一応IDE起動とコマンドプロンプト起動での挙動の違いを再現出来ているわけだから、
>これだけが問題ではないのも事実だ。

そうだよ。精度が変わるのはあなたの間違いではない。スタック領域が0クリア
されようがれまいが、あなたのコード自体には特に不安定さはない。
非初期化領域を参照しているコードは見当たらないし。
239
(1): 2018/09/16(日)16:49 ID:zL1WUjLu(16/27) AAS
>>237
いや、俺が提供した>>191のソースなら使われてるぞ。
>>200のソースでは使われてないが。

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

俺は出来るだけ元のソースのままで追跡しようとしている。
元のソースの該当ケースと離れてしまっては意味がないから。
そして元ソースではsqrtを使っている。
241: 2018/09/16(日)16:54 ID:LrdaMWHl(3/5) AAS
>>237
ああ。また訂正。

sqrt()が使われていないのは、>>200 >>201 >>202 >>203 の場合で、
それは、ループ内にfprintf()を入れた場合と入れない場合とで、
x87 fpuレジスタのst(0)〜st(7)を使う「期間」が変わるために 80BITから
64BITへの書き戻し丸めの問題のために精度が変わっているだけだった。

一方、あなたが指摘した >>191 では、ちゃんと sqrt() 関数が使われていて、
それだと、IDEからの起動とコマンド・プロンプトからの起動とで、精度が変
わってくると。そして、その場合の逆アセンブル結果は >>235 のように
sqrt() 関数がその場で x87 fpu の fsqrt 命令を使わずに、call 文によって
省3
242
(1): 2018/09/16(日)16:56 ID:LrdaMWHl(4/5) AAS
>>239
>いや、俺が提供した>>191のソースなら使われてるぞ。
> >>200のソースでは使われてないが。

了解。

問題を切り分けるため、sqrt() を使わなかった場合の Release版での、
IDE起動とコマンドrライン起動の精度の違いを実験してみて欲しい。
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
省16
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と全く挙動は同じ)

これで有効な指摘については全て回答してるかな?
省8
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 にコピーする。
省14
286
(1): 2018/09/17(月)10:25 ID:+dwRu2dr(3/8) AAS
>>191がコンソール起動とIDE起動で挙動が異なる理由は分かりました。
ありがとう。

結論はつまり以下だ。
> JIT の最適化とデバッグ(抜粋)
> マネージ アプリケーションをデバッグするとき、Visual Studio では、既定で、
> ジャスト イン タイム (JIT: Just-In-Time) コードの最適化が省略されています。
> 最適化されたコードをデバッグするのは困難であるため、
> 最適化されたコードで発生するバグが、非最適化バージョンでは再現しないときにのみお勧めします。
> JIT 最適化は、Visual Studio の [モジュールの読み込み中に JIT 最適化を抑制する] オプションで制御されます。
> 実行中のプロセスにアタッチする場合、既に読み込まれ、JIT でコンパイルされ、
省4
287: 2018/09/17(月)10:25 ID:+dwRu2dr(4/8) AAS
その他諸々、話を整理すると、以下となる。(ソースは>>191参照)
1. managedコードではMSILが出力され、x86コードは含まれていない。
2. 起動時、MSILはJITされ、x86コードに落とされる。
3. このため、mainの1行目でブレークポイントで止め、calc_norm_and_regulateの逆アセンブルを見ようとしても、
 IDE上で「逆アセンブルを表示できません。式がまだネイティブ マシン コードに翻訳されていません。」と出る。
 これはmainの1行目に System::Diagnostics::Debugger::Launch(); を入れたときも同様。
4. そしてこのJITに関して、上記IDE中の 『[モジュールの読み込み中に JIT 最適化を抑制する] オプション』 が効いてくる。
 規定ではオフ、つまり、ReleaseビルドでもIDE起動ならJIT最適化は抑制される。
 これがfld/fmul/fadd/fstpのループコードになる理由。
 これをオンにすれば、確かにReleaseビルドIDE起動でも、
省12
303: 2018/09/17(月)18:30 ID:+dwRu2dr(8/8) AAS
さて俺の本番コード、以下のようだ。
疑問は解消した。協力してくれた皆様ありがとう。

◎:拡張倍精度、○:倍精度、として、(ソースは>>191参照)
・Releaseビルドをコマンドプロンプトから起動→◎積和、◎平方根
・Debugビルドをコマンドプロンプトから起動→◎積和、○平方根
・IDEから起動→○積和、○平方根

これで3種類出来上がってた。
(なお、>>166内バイナリをアタッチした際の「AまたはC」は、「AまたはB」の間違い)
そしてIDE上で『[モジュールの読み込み中に JIT 最適化を抑制する]』を変更すると、
確かにRelease/Debugの2種類に絞れる。
省19
前次1-
スレ情報 赤レス抽出 画像レス抽出 歴の未読スレ AAサムネイル

ぬこの手 ぬこTOP 0.031s