X



Visual Studio 2008 Part 22
0001デフォルトの名無しさん
垢版 |
2014/10/13(月) 15:13:20.18ID:yFRqmPNp
■公式
ttp://www.microsoft.com/japan/msdn/vstudio/default.aspx

■前スレ
Visual Studio 2008 Part 21
http://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
http://peace.2ch.net/test/read.cgi/tech/1404333757/
Visual Studio 2012 Part8
http://peace.2ch.net/test/read.cgi/tech/1392639689/
Visual Studio 2010 Part21
http://peace.2ch.net/test/read.cgi/tech/1412136476/
Visual Studio 2005 Part 27
http://peace.2ch.net/test/read.cgi/tech/1291513609/

その他テンプレ>>2-5
0155デフォルトの名無しさん
垢版 |
2018/04/11(水) 13:06:06.48ID:bG1z7LTF
ああ終わったか
乙でした
0156デフォルトの名無しさん
垢版 |
2018/04/11(水) 13:26:11.45ID:bG1z7LTF
てst
0157デフォルトの名無しさん
垢版 |
2018/04/12(木) 14:51:25.15ID:nqXIWDjI
《 後継開発環境のご案内 》

LinuxMintのダウンロードはこちらから。

Main Page - Linux Mint
https://linuxmint.com/
https://linuxmint.com/download.php
---------

AGK無料試用版の配布開始(リンク先にWindows、Mac、Linux版のファイルが直接置いてある)

AppGameKit - Free Trial Version
https://www.appgamekit.com/trial

無料試用版

AppGameKit無料トライアル版は、AppGameKitの主要な領域すべてにアクセスできるため、
完全に評価することができます。完全版の有料版には、次の主要機能が含まれています。

・ Android、iOS、HTML5にプロジェクトをエクスポートする
・ アプリをデバイスに直接ブロードキャストする
・ コンパイルされたプロジェクトからウォーターマークを削除する
0158デフォルトの名無しさん
垢版 |
2018/04/16(月) 00:56:57.36ID:CadfvnK3
WineでPassmarkとHDbenchを試したところ、シングルスレッド性能はWindowsより若干高く、マルチコア性能は半分程度に留まった。
おそらくWine内部処理の都合と言うことにw

また、DirectXテストは通らなかった。GDIクラスのみ。
0159デフォルトの名無しさん
垢版 |
2018/04/22(日) 03:49:49.80ID:CAx4oxnE
…って言う訳で、早速Linuxのパフォーマンステスト。

64x64ピクセルの2Dスプライトの表示枚数をカウントして、パフォーマンスを計測してみよう。
ダウンロードは以下のリンクからどうぞ。
http://upload.saloon.jp/src/up27011.zip

Firefoxブラウザの右上にダウンロードマーク(↓)の付いたアイコンをクリックして、ファイル
アイコンをクリックすると、zipファイルの入ったフォルダが表示されるので、右クリックして
「 Extract Here 」 を選ぶ。

次に、「 agk_sample 」 と言うフォルダが作成されるので、ダブルクリックする。

フォルダの中に 「 abc 」 と言う拡張子のないファイル、「 media 」 と言うランタイムの入った
フォルダが作成されている。

「 abc 」 と言う拡張子のないファイルが、実行ファイルで、これを迷わずダブルクリックすれ
ばよいw

だいたい2分ぐらいで最大表示枚数が安定する。
プログラムの終了は [ ESC ] キー。

Celeron G1820 + Intel HD無印 で1500枚程度。
0160デフォルトの名無しさん
垢版 |
2018/05/23(水) 19:42:34.90ID:Au5e7VGg
僕の知り合いの知り合いができたパソコン一台でお金持ちになれるやり方
役に立つかもしれません
グーグルで検索するといいかも『ネットで稼ぐ方法 モニアレフヌノ』

CRP7I
0162デフォルトの名無しさん
垢版 |
2018/05/25(金) 19:20:37.97ID:TsdeULHr
ではではw

Intel Pentium G5 CoffeeLake 総合スレ
http://anago.open 2ch.net/test/read.cgi/jisaku/1526884213/
0163デフォルトの名無しさん
垢版 |
2018/07/04(水) 22:48:21.01ID:gFgZc5FG
TJH
0166デフォルトの名無しさん
垢版 |
2018/09/13(木) 23:46:46.29ID:MPXnmy/B
起動の仕方で浮動小数点演算に誤差が発生するのだけど、理由が分かる人居ますか?

今のところ、

A. Debugビルドのバイナリをダブルクリックして起動
B. Releaseビルドのバイナリをダブルクリックで起動
C. DebugビルドまたはReleaseビルドでF5で起動

の3種類出来てる。それぞれ微妙な誤差だ。(doubleの下位ビット)
AとBの違いについては諦めていたのだが、(なお両方とも/fp:precise)
実はCもあると気づいてしまった。

なお、バイナリをダブルクリックして起動、それにIDEをアタッチしても結果は変わらない。
それぞれAまたはCが出る。

何かIDEの設定を間違っているのだと思うのだけど…
0167デフォルトの名無しさん
垢版 |
2018/09/14(金) 12:43:29.26ID:5xDSXwp0
0fillしてないんだろ
0168デフォルトの名無しさん
垢版 |
2018/09/14(金) 13:05:12.93ID:XcO59d50
再現性は100%なので、初期化のミスではない。(と思っている)

演算途中の結果を double なら16進16桁でテキストにダンプしている。
数値だけではないが500MB程度のファイルが出力され、
diff を取ることによりレグレッションテストをしている。
今のところ、A/B/Cの3種類しか出ない。
初期化忘れならこうはならない。(はず)
0169デフォルトの名無しさん
垢版 |
2018/09/14(金) 13:09:49.07ID:Qw2nkUqU
MSのエンジニアを信じるか、再現可能なソースも示さず問題だ問題だ言ってるとこの馬の骨とも知れない奴を
信じるかって言われたら迷うことなく前者を信じるw
0170デフォルトの名無しさん
垢版 |
2018/09/14(金) 14:43:10.29ID:XcO59d50
俺はIDEのバグだとは思っていない。
何かIDEの設定があって、俺がそれを適切にやってないのだと思っている。

色々見た限り、俺は /fp:precise しか発見出来なかった。
ただしこれは一応適切に設定されている。
他にないかな?ということ。
0171デフォルトの名無しさん
垢版 |
2018/09/14(金) 15:49:43.21ID:cyu8WCuc
IDEじゃなくてコマンドラインからコンパイラで直接コンパイルしても一緒?
0172デフォルトの名無しさん
垢版 |
2018/09/14(金) 16:14:47.18ID:XcO59d50
どういう意味?
(どれとどれの違いにフォーカスしろと?)

俺はバイナリに問題があるとは思っていない。
何らかの理由で浮動小数点のモードが切り替えられたりしてないかを疑ってる。
だからIDEの設定とか、そっちを見てる。

Release/Debugのバイナリは当たり前だが違ってる。
/O2と/Od等だ。
ただそれでも /fp:precise の場合は
浮動小数点の演算順序を入れ替えない範囲で最適化される事になっている。
逆アセンブラは見たけどちょっとグチャグチャすぎてよく分からなかった。
(見た範囲では演算順序の入れ替えはないように思えた)
0173デフォルトの名無しさん
垢版 |
2018/09/14(金) 16:25:44.73ID:2giHKuhd
>>166
double型を使っていても、SSEのXMMレジスタなどを使う場合、昔のfld, fst, fmulなどを使う場合より基本的には精度が落ちる。

丸めの方向も四捨五入、正負どちらかの方向への丸めや切り捨てなどの他、確か、精度を気にしない、なんてオプションもあった気がする。ゲームで使うことを想定しているらしい。

SSE用に最適化を掛けると、速度は上がるが精度は落ちるかも知れない。
0174デフォルトの名無しさん
垢版 |
2018/09/14(金) 17:45:18.51ID:XcO59d50
>>173
今の範囲ではSSEは使っていない(はず)
「拡張命令セットを有効にする」は「設定無し」になっている。(多分デフォのまま)
見た目全部x87が出てたし。

やっぱRelease/Debugの違いから攻めろって感じか?
俺もF5起動で結果が異なるってのは全く予想してなかったし。(ただしこちらの問題だろうけど)
演算部分は場合によってはSSEのアセンブラに差し替えるので、
問題になる場合には、Release/Debugの違いはこれで吸収するつもりだった。
(これは1ヶ月後にやるかも)
0176デフォルトの名無しさん
垢版 |
2018/09/14(金) 21:39:03.40ID:fXySkelb
再現するコードをみないとなにもわかるわけがない
低学歴知恵遅れが書くコードなんかなにをやってるか分からないからな

ごちゃごちゃいってないで再現するコードをあげなさい
0177デフォルトの名無しさん
垢版 |
2018/09/15(土) 06:26:55.94ID:/OsufeBT
月並みな意見だけど、
「症状を再現できる最小のソースと初期データ、それとコンパイル環境」
まで切り詰めてみるのが早道じゃないかな。

ちょくちょく発生する事例なら、解決策を知ってる誰かが教えてくれてるかと。
週末、より多くの人が質問を見ることを期待して待つ手もあるけど。
0178デフォルトの名無しさん
垢版 |
2018/09/15(土) 08:56:57.28ID:9ZmI9OgI
より基本に立ち返ろう
そもそも誤差はあるのか?

計算結果を何を使ってどう出力しているかだけでもソースを見せてくれ
0179デフォルトの名無しさん
垢版 |
2018/09/15(土) 13:36:34.65ID:heijdb7v
>>174
x87でも誤差を丸める方法を fpu control word で設定できる事は出来る。
四捨五入と切り捨てなどを切り替えられる。
0180デフォルトの名無しさん
垢版 |
2018/09/15(土) 13:42:47.95ID:heijdb7v
fpu control word で、丸め方法と、精度の二つを独立に設定できる。

この精度の設定で、メモリ上の表現がdouble型でも、計算時のfpuの内部精度が変わってくる。

普通は64bit doubleに対して、fpu内部では80bitの精度で計算する。仮数部のbit数は、確か、それぞれ、53BIT、64BITだったかな。
0181デフォルトの名無しさん
垢版 |
2018/09/15(土) 14:05:09.61ID:heijdb7v
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.
0182デフォルトの名無しさん
垢版 |
2018/09/15(土) 14:14:58.92ID:heijdb7v
>>181
書いてある意味は、

「SSEやSSE2だと、float(32BIT)やdouble(64BIT)のまま計算するが、x87 fpuだと、もっと高い精度であるところの
  『double extended-precision floating-point format(拡張倍精度浮動小数点フォーマット:80BIT)』
で計算を実行して、丸めてから、floatやdoubleに戻す。
そのため、SSE/SSE2 と x87 fpuでは結果が変わることがある。
しかし、その場合でも結果の違いは、仮数部の LSB (最も価値の小さいBIT)の1BITにだけ現れる。」

というような事。
0183デフォルトの名無しさん
垢版 |
2018/09/15(土) 16:09:28.75ID:aC3C7hdp
揚げ足取りだけどLSBを「最も価値が小さい」って直訳はいかがなものかなw
普通に最下位ビットじゃないの?

ところで
> the least-significant bits
これ何で複数形なのかね
0184デフォルトの名無しさん
垢版 |
2018/09/15(土) 16:14:15.89ID:AVfR6YnT
2の0乗だから実際価値が低い

上はMVBで
0185デフォルトの名無しさん
垢版 |
2018/09/15(土) 17:07:56.98ID:UR1d6CKz
>>182
ありがとう。
ただ一応それは知ってて、その上で、IDEの環境設定等を疑っている。

>>177
とはいえ地道に辿って、同様の再現コードを作ることに成功しそうだ。
もう少し調べて、多分今晩か明日午前中に上げる。
0186デフォルトの名無しさん
垢版 |
2018/09/15(土) 17:12:32.47ID:heijdb7v
>>183
「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 という英語になる。
0187デフォルトの名無しさん
垢版 |
2018/09/15(土) 17:15:04.14ID:heijdb7v
誤:これは、SSE の場合の LSB と、x87 FPU の場合の FPU の2種類を
正:これは、SSE の場合の LSB と、x87 FPU の場合の 2種類を
0188デフォルトの名無しさん
垢版 |
2018/09/15(土) 17:18:55.19ID:heijdb7v
>>183
>揚げ足取りだけどLSBを「最も価値が小さい」って直訳はいかがなものかなw
>普通に最下位ビットじゃないの?

整数の場合は、「最下位ビット」というと、本当にBITの位置が一番右にあるようなイメージもある。

一方、浮動小数点数の場合は、右にあるとかより、仮数部において、一番価値の小さいビット、というニュアンスを使えたかった。

IEEEでは、それは確かに一番右にあるビットであって、マシン語レベルの表現では、BIT0ではあるのだが。

ニュアンス的に。
0190デフォルトの名無しさん
垢版 |
2018/09/15(土) 20:35:40.44ID:UR1d6CKz
同様の症状を再現出来るようになったので上げる。
完全再現は出来てないが、使い物にはなるはず。興味がある人はよろしく。

症状:
単独で起動した場合と、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の結果が得られる。
0191デフォルトの名無しさん
垢版 |
2018/09/15(土) 20:37:06.43ID:UR1d6CKz
ソース:
#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;
}
0192デフォルトの名無しさん
垢版 |
2018/09/15(土) 20:42:48.06ID:UR1d6CKz
オプション等(コマンドライン):(全てデフォのままのはずだが一応)
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。
0193デフォルトの名無しさん
垢版 |
2018/09/15(土) 20:43:03.84ID:UR1d6CKz
備考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
0194デフォルトの名無しさん
垢版 |
2018/09/16(日) 02:47:43.28ID:wIV2HUNW
>>190
C++/CLR では、.Net を使っているから、起動方法が違うだけでも、
fpu control word の値や、使うCPU命令がx87 FPUなのか、SSE
なのかが違ってくる可能性があるかもしれない。

fpu control word は、main()関数に入る前の start up codeの中で
初期化される。
0195デフォルトの名無しさん
垢版 |
2018/09/16(日) 03:19:36.22ID:wIV2HUNW
>>191
// Release build
// 0.000000, 0x1ff68ddfb62221dd from IDE
// 0.000000, 0x1ff68ddfb62221de from command prompt

それにしても、随分小さな値だね。ちなみに、浮動小数点表示
の場合の有効数字の桁数を上げたら、どのようになる?
1.xxxe-yy
表示にして。
0196デフォルトの名無しさん
垢版 |
2018/09/16(日) 03:40:33.66ID:wIV2HUNW
>>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) );

としてみるとどうなる?
0197デフォルトの名無しさん
垢版 |
2018/09/16(日) 03:42:01.82ID:wIV2HUNW
>>196
誤: printf( "%30.30e, 0x%016X\n", norm, *(__int64*)&norm) );
正: printf( "%30.30e, 0x%016X\n", norm, *(__int64*)&norm );
0198デフォルトの名無しさん
垢版 |
2018/09/16(日) 07:27:22.50ID:SOVIz+sV
> 0x1ff68ddfb62221dd(Debug)
> 0x1ff68ddfb62221de(Release)

VS 2010 VC++ Express でも再現した
0199デフォルトの名無しさん
垢版 |
2018/09/16(日) 07:38:10.70ID:SOVIz+sV
↓このループを抜けたあと、すでに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];

とりあえずまずこれだけは分かったから
低学歴知恵遅れが書いたウンココードの問題箇所を限定する
0200デフォルトの名無しさん
垢版 |
2018/09/16(日) 07:51:03.41ID:SOVIz+sV
@-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;
}
0201デフォルトの名無しさん
垢版 |
2018/09/16(日) 07:51:56.62ID:SOVIz+sV
@-1 デフォルト設定(Release)

↓このコードの逆アセンブルコード
https://ideone.com/Ryyxkl

【実行結果】

0x0007F2C44DFFF8F2:1.1053482540585106e-308
0202デフォルトの名無しさん
垢版 |
2018/09/16(日) 07:53:20.79ID:SOVIz+sV
@-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;
}
0203デフォルトの名無しさん
垢版 |
2018/09/16(日) 08:02:12.99ID:SOVIz+sV
@-2 デフォルト設定(Release)

↓このコードの逆アセンブルコード
https://ideone.com/4j12ib

【実行結果】

↓実行結果を書き込めないからこっちに書き込んどいた
https://ideone.com/gOqtki

0x0007F2C44DFFF8F1:1.1053482540585101e-308
0204デフォルトの名無しさん
垢版 |
2018/09/16(日) 08:05:50.92ID:SOVIz+sV
A-1 最適化無効 (/Od)(Release)

※ コードは@-1(>>200)と同じ

↓このコードの逆アセンブルコード
https://ideone.com/cONCAx

【実行結果】

0x0007F2C44DFFF8F1:1.1053482540585101e-308
0205デフォルトの名無しさん
垢版 |
2018/09/16(日) 08:09:59.78ID:SOVIz+sV
A-2 最適化無効 (/Od)(Release)

※ コードはA-2(>>202)と同じ

↓このコードの逆アセンブルコード
https://ideone.com/kuRt3w

【実行結果】

※ @-2(>>203)と同じ

0x0007F2C44DFFF8F1:1.1053482540585101e-308
0206デフォルトの名無しさん
垢版 |
2018/09/16(日) 08:20:27.67ID:SOVIz+sV
@-1、@-2の逆アセンブルの出力結果を比較すると原型をとどめてないぐらいグチョグチョに違う(最適化のせいと考えられる)
A-1、A-2の逆アセンブルの出力結果を比較すると差異はほとんどない(Aは両方ともまったく最適化されてないから当然)

@-1とA-1の逆アセンブルの出力結果を比較すると原型をとどめてないぐらいグチョグチョに違う(@-1のコード(>>200)ははげしくウンコ最適化されてると考えられる)
@-2とA-2の逆アセンブルの出力結果を比較すると差異はほとんどない(@-2のコード(>>202)はあまり最適化されてないと考えられる)

はっきりいって、これ以上見る気もしないしテキトーだが
ウンコみたいな最適化で演算の順序が入れ替わったせいで、誤差が発生しているものと考えられる
0207デフォルトの名無しさん
垢版 |
2018/09/16(日) 08:28:37.25ID:zL1WUjLu
>>198以降、
すまん、入れ替わりになるかもしれんが後で確認する。
まず>>194その他について回答する。

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

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

>>196
.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)

ただ当たり前だが、書式を変えたところで計算結果は変わらないし、
精度の問題には関係ない。
0208デフォルトの名無しさん
垢版 |
2018/09/16(日) 08:29:03.14ID:zL1WUjLu
>>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方向への切り捨て
0209デフォルトの名無しさん
垢版 |
2018/09/16(日) 08:29:57.53ID:zL1WUjLu
>>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その他について確認する。
0211デフォルトの名無しさん
垢版 |
2018/09/16(日) 13:21:26.16ID:haV9TZ8e
>>209
>命令自体は 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 );

を使ってみたらどうなる?
0212デフォルトの名無しさん
垢版 |
2018/09/16(日) 13:31:15.50ID:Q5j4SiHR
win32コンソールなら結果が同じ。 もう理由は分かったのに何が問題なんだ?こんなの何の影響もないだろう。
0213デフォルトの名無しさん
垢版 |
2018/09/16(日) 13:33:14.92ID:zL1WUjLu
>>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と同じコードだからだ。
勿論結果も同じだった。
0214デフォルトの名無しさん
垢版 |
2018/09/16(日) 13:33:48.22ID:zL1WUjLu
>>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関連のことはこちらも知っている上で、質問している。
0215デフォルトの名無しさん
垢版 |
2018/09/16(日) 13:53:17.67ID:haV9TZ8e
>>213
なるほど、全く別の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 を読み取ってみると、不安定な値が読み出されたと。
0216デフォルトの名無しさん
垢版 |
2018/09/16(日) 13:58:15.86ID:haV9TZ8e
まず、
__asm{
 fnstcw [cw];
}
ではなく、_control87() を使ってみて欲しい。

インラインアセンブラは、独立した *.asm で書くより危険な場合が
あるかも知れないので。特にC関数の引数、今の場合は、「cw」を
インライン・アセンブラで用いた場合、正しいコードが出ているかどうか
は注意が必要。
0217デフォルトの名無しさん
垢版 |
2018/09/16(日) 14:05:28.41ID:haV9TZ8e
>>208
よく見ると、それは、かなり複雑な事情が絡みそうなコード。
以下のようにした方が安心。なお、「cw」という短すぎる引数名
も長年のプログラミング経験からすると、インラインアセンブラでは
怖い。また、

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
0218デフォルトの名無しさん
垢版 |
2018/09/16(日) 14:17:40.34ID:haV9TZ8e
あ、後、インライン・アセンブラで実験する場合は、関数名の inline は
「取った」方がいい。つまり、以下の方が安心:

#pragma unmanaged
void fpu_getcw(unsigned short *pCW) {
 __asm{
  mov edx,pCW
  fnstcw [edx];
 }
}
#pragma managed
0220デフォルトの名無しさん
垢版 |
2018/09/16(日) 15:37:02.44ID:Q5j4SiHR
.netはx87コンテキストをすべて保持しませんって分かったんだからもう十分。
win32かx64にすれば解決。そもそも問題になる仕様バグじゃない。
0221デフォルトの名無しさん
垢版 |
2018/09/16(日) 15:40:56.17ID:haV9TZ8e
>>219
ホントだ。/MDだと、Runtime Library として DLL のものを使ってしまう。
これは、今回の現象に非常に重要な影響を与えているかも知れない。
0223デフォルトの名無しさん
垢版 |
2018/09/16(日) 15:48:45.63ID:zL1WUjLu
>>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.
> https://msdn.microsoft.com/en-us/library/e9b52ceh.aspx

とはいえ無理矢理やってみた。警告は出るがコンパイルは通る。
結果は、どこに置いても、Debug/Releaseでも、常に 0x9001f が読み出される。
ただし、これは上記の仕様からして、当てにならない。
0224デフォルトの名無しさん
垢版 |
2018/09/16(日) 15:49:44.99ID:zL1WUjLu
>>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でもしているのか?
まあこれに当たっているのなら確実に俺のバグだし、これなら辻褄は合ってしまうのだが。
0225デフォルトの名無しさん
垢版 |
2018/09/16(日) 15:51:01.50ID:zL1WUjLu
>>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の一部しか使えない、ということか?
0226デフォルトの名無しさん
垢版 |
2018/09/16(日) 15:51:25.16ID:zL1WUjLu
>>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] だ。
理由は分からんがこちらだと安定しているので、結果としてはこのやり方が正しい。
0227デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:02:47.64ID:haV9TZ8e
>>225
をを。やはり、ある意味ではVCが間違ったアセンブリコードを出していたよ。
それだと、
fnstcw [EBP+08]
という意味になってしまって、
fnstcw pCW
の意味になっている。つまり:
pCW = control_word;
あなたが、やりたいのは、
*pCW = control_word;
だったのだから、アセンブリ・コードが間違ってる。

あなたが指示したのは、
fnstcw [pCW]
だった。実際に生成されたコードは、
fnstcw pCW
だった。

VC のインラインアセンブラは、エラーも出さずに間違ったコードを
出すことが証明された。

これと、精度が不安定な問題とは全く別ではあるけれど。
0228デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:06:05.39ID:zL1WUjLu
すまん、間違いの修正

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

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

俺にとっては一つ新しい知見として、
・IDEから起動した場合、スタックが初期化されるっぽい
ということが分かった。とはいえOSは0fillしてから各プロセスにメモリを与えるので、実際は、
・コマンドプロンプト起動ならmain前に設定した続きでそのまま実行、
・IDE起動ならmain前に色々やって0fillして実行、
 或いはmain前に色々やることが多く、スタックが進み、(例えばデバッガをアタッチする為)
 結果的にOSが初期化済みの領域から始動
となって違いが発生するというところか。
0229デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:11:52.18ID:haV9TZ8e
>>225
>正直、/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]

というコードにしなくてはならなかったのに、VCがある意味では間違った。
これが、単独の *.asm ではなく、VC の asm {・・・} が危険な理由。
VC の asm は特に危険。
0230デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:17:02.07ID:haV9TZ8e
>>228
>ただし>>191の再現コードで『不定スタック領域』を掴んでいるわけもなく、
>一応IDE起動とコマンドプロンプト起動での挙動の違いを再現出来ているわけだから、
>これだけが問題ではないのも事実だ。

そうだよ。精度が変わるのはあなたの間違いではない。スタック領域が0クリア
されようがれまいが、あなたのコード自体には特に不安定さはない。
非初期化領域を参照しているコードは見当たらないし。
0231デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:20:22.27ID:haV9TZ8e
逆アセンブラ結果を見てないで言うけど、もし、sqrt() が call文で関数呼び出し
されているんだったら、そこで精度の違いが出てるかもしれない。
0232デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:23:20.75ID:zL1WUjLu
>>227
なるほど、了解した。
つまり、>>209は全面的に間違いで、正しくは、

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

だな。


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

VCの問題ではなくて、
俺が fnstcw [cw] と書いたのが間違いで、それをそのままコードにされてしまっただけだな。
正しく書けばSyntaxErrorだったのだし。
なお fnstcw [*cw] もSyntaxErrorだ。手動で一旦レジスタに移さないと駄目だな。
全くもって>>218のコードが正しい。
0233デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:35:08.59ID:LrdaMWHl
>>232
>俺がやるべきだったのは 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;

としたということ。
0234デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:36:04.13ID:zL1WUjLu
>>229-230
了解だ。ありがとう。

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

ただし、
> そこで精度の違いが出てるかもしれない
との繋がりがよくからない。
sqrt()でcallされると、スタックが改変される。おそらくデータ依存か?
なら未初期化のスタックを掴みに行っているコードが有ればバグる。
ただし今回の『再現コード』はこの限りではない。
(俺の本番コードはさておき)
0235デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:37:19.30ID:zL1WUjLu
>>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]
0236デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:37:34.94ID:zL1WUjLu
>>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]
0239デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:49:54.65ID:zL1WUjLu
>>237
いや、俺が提供した>>191のソースなら使われてるぞ。
>>200のソースでは使われてないが。

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

俺は出来るだけ元のソースのままで追跡しようとしている。
元のソースの該当ケースと離れてしまっては意味がないから。
そして元ソースではsqrtを使っている。
0240デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:53:58.12ID:/oSJzlqn
たぶん2008の最適化ミスだと思う。
static double norm = 0;// ←"static"を追加する
にするとか、最適化オプションをいじると
Release/コマンドプロンプトからの起動でも
0x1ff68ddfb62221ddになる
0241デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:54:10.71ID:LrdaMWHl
>>237
ああ。また訂正。

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

一方、あなたが指摘した >>191 では、ちゃんと sqrt() 関数が使われていて、
それだと、IDEからの起動とコマンド・プロンプトからの起動とで、精度が変
わってくると。そして、その場合の逆アセンブル結果は >>235 のように
sqrt() 関数がその場で x87 fpu の fsqrt 命令を使わずに、call 文によって
実際に本当のサブ・ルーチンを呼び出していると。

これはとても興味深い。そのサブ・ルーチンの中が、時と場合によって
精度が変わってくるような書き方をされている可能性が見えてきた。
0242デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:56:42.67ID:LrdaMWHl
>>239
>いや、俺が提供した>>191のソースなら使われてるぞ。
> >>200のソースでは使われてないが。

了解。

問題を切り分けるため、sqrt() を使わなかった場合の Release版での、
IDE起動とコマンドrライン起動の精度の違いを実験してみて欲しい。
0244デフォルトの名無しさん
垢版 |
2018/09/16(日) 17:22:07.21ID:zL1WUjLu
>>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
0245デフォルトの名無しさん
垢版 |
2018/09/16(日) 17:22:29.32ID:zL1WUjLu
>>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]
0246デフォルトの名無しさん
垢版 |
2018/09/16(日) 17:22:46.06ID:zL1WUjLu
>>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
0247デフォルトの名無しさん
垢版 |
2018/09/16(日) 17:35:35.70ID:zL1WUjLu
>>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分くらいでちょっと離れる予定。
0248デフォルトの名無しさん
垢版 |
2018/09/16(日) 18:30:34.19ID:HF0YmRsW
>>212
ほんそれ
0249デフォルトの名無しさん
垢版 |
2018/09/16(日) 20:54:27.39ID:zL1WUjLu
>>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]
0250デフォルトの名無しさん
垢版 |
2018/09/16(日) 21:25:23.90ID:zL1WUjLu
>>219
>>221
/MTと/clrは同時に指定出来ないらしい。(error D8016)
/MTdも同じく無理。

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

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


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


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

・Releaseビルドをコマンドプロンプトから起動したときのみなぜか精度が高い
 (>>200から結果的に検出された。今のところ精度が高いときと同じ挙動をしている為)
・ReleaseビルドもIDEから起動すれば結果的にスタックが0初期化されている状態になっており、
 俺の本番プログラムに関してはここに当たるバグがある?(>>228)
 (ただしこれは>>191には該当しない)
0251240
垢版 |
2018/09/16(日) 21:43:04.47ID:/oSJzlqn
.netの場合、デバッガ配下では(デバッグのため)違うコードを実行しているような気がする。
デバッガの逆アセンブル表示とかasm出力はあまり当てにならないような気もする。
ループ部分だけど、レジスタのみで処理するか、メモリを使用するかで精度が変わるのかも。
そもそも、どっちが正しいのかよくわからんけど...
ループ部分の関数を#pragma unmanagedすると結果が変わるでそれが正しいのかも。
0252240
垢版 |
2018/09/16(日) 21:43:27.78ID:/oSJzlqn
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
0253デフォルトの名無しさん
垢版 |
2018/09/16(日) 22:27:34.58ID:zL1WUjLu
>>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)
0254デフォルトの名無しさん
垢版 |
2018/09/16(日) 22:33:32.07ID:zL1WUjLu
>>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は何故か直ってしまった。
0255デフォルトの名無しさん
垢版 |
2018/09/16(日) 22:34:16.44ID:zL1WUjLu
>>252
> .netの場合、デバッガ配下では(デバッグのため)違うコードを実行しているような気がする。
> デバッガの逆アセンブル表示とかasm出力はあまり当てにならないような気もする。
これは俺も相当疑っているのだが、今のところ尻尾を掴めない。
ILspyだっけ?外部の逆アセンブルツール使えばチェック出来るのかな?

いずれにしても、>>251の指摘
・unmanagedにすれば直る
のも事実だし、逆アセンブルを見る限り、これを説明出来る理由もないのも事実。
レスを投稿する


ニューススポーツなんでも実況