C++相談室 part144
■ このスレッドは過去ログ倉庫に格納されています
C++に関する質問やら話題やらはこちらへどうぞ。 ただし質問の前にはFAQに一通り目を通してください。 IDE (VC++など)などの使い方の質問はその開発環境のスレにお願いします。 前スレ C++相談室 part143 https://mevius.5ch.net/test/read.cgi/tech/1560574313/ このスレもよろしくね。 【初心者歓迎】C/C++室 Ver.105【環境依存OK】 https://mevius.5ch.net/test/read.cgi/tech/1556142878/ ■長いソースを貼るときはここへ。■ http://codepad.org/ https://ideone.com/ [C++ FAQ] https://isocpp.org/wiki/faq/ http://www.bohyoh.com/CandCPP/FAQ/ (日本語) ----- テンプレ ここまで ----- >>387 > まあクロスの場合開発環境側のintな気もするが そんなアホなw いや、多分そこまで考えてないから、コンパイラ上ではintもしくはsize_tで マクロ展開時は文字列化して接尾の型指定とかつけないだろ 勝手につけられると逆に困るし int型はターゲットアーキテクチャーで一番自然な型というのが本来の意味なのでソースコードの最大行数と関係する理由が無い C++の最新規格ではどうなったかわからんが、longは規格上32 bit固定なのに対してintは16 bitのアーキテクチャーが有り得るはず longが32bit固定なシステムは64bitでは異端じゃね まあwindowsはLLP64っぽいが バイナリファイルを一度に読み込もうとしています。 圧縮ありファイルと圧縮なしファイルを以下のように判定して読み込もうとしているのですが、 ファイルの全てを読み込む方法がわかりません。 @Aはどのようにしたら良いですか? http://codepad.org/FwYwQ62R std:: これ自体は大した量ではないとは思うけど これが何百何千と書かないといけないなら省略できる方法は必要だと思う 省略して且つ安全で簡単な方法が無いのが問題なんだと思う ここまで読んででusing宣言を書き連ねたファイルを用意して 関数定義の次の行で#includeするのが今の所一番良い様な気がした const何かも余りに書きすぎるので これも何らかの形で省略できる記法を搭載できないのだろうかとは思ってる classのpublic: みたいに const: とかするとその後は記載しなくてもconst宣言になる みたいな方法を搭載したりはしないのか? と何時も疑問に思う >>396 constこそusing書き連ねたファイル用意してincludeすればいいのでは using cchar = const char; using cuchar = const unsigned char; using cint = const int; using cuint = const unsigned int; >>396 その疑問の答えは、あなたが具体的な提案を標準化委員会に持っていかない理由の答えと同じでしょう。 >>397 ありがとうございます。 とりあえず圧縮なしの方は以下で取得できたようです。 ここで一点疑問です。 mallocの値がぴったり正しいかの確認をしようと、下記5行目のdatasize+1を-2や-3にしたのですが、 printf()で最後まで値が正常に出てきます。 datasize-3としてもたまたま連続して拾っているだけなので問題ありだと思うのですが、 malloc(datasize)ではなく、malloc(datasize+1)でぴったりメモリ確保ができているのでしょうか? unsigned char* data; long datasize; fseek(fp, 0, SEEK_END); datasize = ftell(fp); data = (unsigned char*)malloc(datasize+1); // +1を-2, -3にしてもprintf()では最後まで正常な値が出てくる。 if( data == NULL ){ exit(1); } fseek(fp, 0, SEEK_SET); fread(data, 1, datasize, fp); fclose(fp); for(int i=0; i < datasize; i++){ printf("%02x\n", data[i]); } 割り当てされてない領域に書き込むというのは、C/C++ではあり得る。 そこに他に意味のあるデータがあれば、そのデータは壊れる(データ破壊)。 >>402 処理系によるけど大抵の処理系では指定されたサイズをきっちり確保とかはしなくて16バイト単位くらいで確保する なのでそのdatasizeの値によっては-15とかしても大丈夫な場合すらあることがある もちろん動作は保証されない > malloc(datasize)ではなく、malloc(datasize+1)でぴったりメモリ確保ができているのでしょうか? malloc(datasize)で十分、malloc(datasize+1)だと1つ余分 freadでもprintfでもdatasize個までしかアクセスしていないので 昔みたいにメモリ枯渇なんてそうないし誰かがデータ破壊やらかして休みに呼び出し食らいたくないので16バイトくらい余計に確保しとこ >>404 malloc()は数を指定していて、for分でアクセスしているdataは配列だから最後はdatasize-1ということですね。 僕の理解と合いました。 malloc(datasize)とします。 ありがとうございます。 次にgzipの全読み込みも調べています。 とりあえず下記でout.dbと展開後のaaa.dbのcksum値が一致したのでちゃんと読み込めたと思います。 ※1のところで配列に格納すれば良いと思うのですが、最初は配列の数が確定していないので 配列数を決定することができません。 読み込むファイルサイズはギガバイトクラスの大きなファイルなので、なるべく速度を維持したまま 一つの配列に格納したいのですが、どうすれば良いですか? FILE *wfp; gzFile rzp; unsigned char buf[8192*1000] = {0}; rzp = gzopen("aaa.db.gz", "rb"); wfp = fopen("out.db", "wb"); while((cnt = gzread(rzp, buf, sizeof(buf))) > 0){ fwrite( buf, sizeof( unsigned char ), cnt, wfp ); ※1 ここで全データを一つの配列に格納したい。 } gzclose(rzp); fclose(wfp); gzipって全部読まないと展開後の長さ分からないんじゃね 2パスにするとか qiitaでさC++のタグ検索したらcppreference.comが公式リファレンスと書かれてたけどここ公式なの? >>413 https://ja.cppreference.com/w/Cppreference:FAQ このサイトの背後にいるのは誰ですか? cppreference.com は世界中の C++ 愛好家のグループによって作成され、メンテナンスされています。 >>412 コメントありがとうございます。 全部読む以外に調べる方法は無いんですね。 ということでrealloc()で拡張していくことを考えてみました。 下記で作られた解凍後のデータがgunzipで解凍したものと一致したので大丈夫そうです。 ありがとうございました。 #define MAX_BYTE 8192*1000 data = (unsigned char*)malloc(MAX_BYTE); rzp = gzopen("aaa.db.gz", "rb"); long data_size = 0; while((cnt = gzread(rzp, data+data_size, MAX_BYTE)) > 0){ data_size += cnt; data_new = (unsigned char*)realloc(data, data_size+MAX_BYTE); if( data_new == NULL ){ free(data); ::fprintf(stderr, "拡張失敗\n"); exit(EXIT_FAILURE); } // アドレスが変化した場合 if( data != data_new ){ // free(data); // 元オブジェクトは解放済みのため不要 data = data_new; } } gzclose(rzp); // 解凍後データを書き込み wfp = fopen("out.db", "wb"); fwrite( data, sizeof( unsigned char ), data_size, wfp ); fclose(wfp); >>415 そんなでかい領域をreallocすると何度もコピーするはめになるから>>412 の言うように2パスにするか、各領域をリストで繋いて最後に一気にコピーするようにした方がいい >>416 2パスをググってもそれらしいものがなかったのですが、どういうものなのでしょうか? あと、「各領域をリストで繋いて」というのはvectorに追加しながら最後に連結するということでしょうか? >>417 > 2パスをググってもそれらしいものがなかったのですが、どういうものなのでしょうか? 1回目(Pass 1)ではデータの格納はせずにサイズだけ取得する 合計サイズ分を確保して2回目(Pass 2)で実際に格納するってこと > あと、「各領域をリストで繋いて」というのはvectorに追加しながら最後に連結するということでしょうか? そう言うこと でかいデータはコピーにも時間がかかるからできるだけコピーしないようにした方がいい >>417 2パス良さそうですね。 gzread(rzp, data+data_size, MAX_BYTE) のところを下記のようにNULLにしてもちゃんとファイルの最後まで動いているようなのですが、これがデータ格納せずにサイズを取得するということでしょうか? gzread(rzp, NULL, MAX_BYTE) すみません、NULLだとそもそも一回も取得できずに終わっていました。。 データ格納せずに一回ファイル精査するのってどうするんでしょうか? >>420 固定長のバッファを1つ用意して、そこに上書きしながら繰り返し読み込めばいい。内容は上書きされて読めなくなるけど、読み捨てるつもりなので気にしない。 (最終的なところに)格納しないってことな 適当なバッファを1つ用意して毎回そこに格納しとけばいい >>421 ,422 そういうことですか、理解できましたありがとうございます。 vectorだって償却定時間なのに ホントに2回読む方が速いんか? valgrindってみんな開発に使ってるの?死ぬほど遅いんですが でかい領域のreallocで毎回コピーが発生するというのは都市伝説、 と言いたいところだが他のスレッドが動いているとそうとも言い切れないかそうか、 この場合の2パス方式はgzipが2回走るのがいかにも実行時間の無駄でいやすぐる 最初に大きめの領域をmallocして足りんかったら2パスに切り替えるという投機的なやり方のが良い だいたいいかにgzip様といってもファイルを毎回1/10まで圧縮できるわけはないのだから 読み込むファイルサイズの10倍を確保しておけばほぼ1パスで済む 展開後の実サイズがわかったら展開後の実サイズぴったりにreallocで縮小すれば良い ちなメモリの断片化はガン無視 実使用サイズの倍のメモリも用意できないようなプアな環境のならmallocやreallocする設計から見直さねばならない 見直すべき >>427 意味わからん スレッドなんて関係ないだろ >>429 ヒープメモリはスレッドで共有される もしヒープメモリを使う他のスレッドが一切無いなら realloc()でサイズ拡大してもコピーは生じない(後ろに領域伸ばせるから この説明でまだ疑念が渦巻くのならプログラミング言語Cをきちんと読み直した砲が良い (あのmalloc()実装例がmalloc()実装の全てとは言わんが基本 >>430 断片化って知ってる? > 後ろに領域伸ばせるから これ常にできるわけじゃないよ > この説明でまだ疑念が渦巻くのならプログラミング言語Cをきちんと読み直した砲が良い お前がなw 特にこの場合はgzipを使うからね アウトプット用のメモリを確保した上でgzipを実行するわけだけど gzipが内部で動的にメモリを確保するかしないかは知らんけど、多分するでしょ そうすると新たに論理アドレス空間が使われるから 連続したメモリ空間を後ろに拡張できるかって言われるとね >>431-433 んまーそれはそうやったorz あと今回のgzip展開ルーチン呼び出し前にmallocした領域がたまたま後ろに居る可能性もあるな! しかし>>428-428 は断片化が生じないと言っているのではないし主目的のgzip読み込みに関しては 断片化による影響を受けにくくしているのだから大枠問題無い 良い良い、良いではないか 色々とご意見ありがとうございます。 どなたかの役にたてばと思い僕が調べた内容をお知らせします。 gzip展開後に5GBくらいになるデータでテストしてみました。 @time ./a.outで簡易的に時間取得 Arealloc方式の場合 >>415 の下記部分でコピーが何回発生しているかをカウントしてみる // アドレスが変化した場合 if( data != data_new ){ // free(data); // 元オブジェクトは解放済みのため不要 data = data_new; copy +=1; // コピーした回数をカウント }else{ not_copy +=1; // コピーせずに拡張できた回数をカウント } 結果は以下のようになりました。 強力なサーバでメモリが潤沢にあるためか、コピーがほとんど発生しないようで realloc方式が早かったです。 本日祝日でサーバ使用者が少なく、よりコピー回数が減っていると思います。 個人PCでchromeにメモリ食われたりするとコピーが増えて逆転するかもしれないですね。 環境に合わせて変えようと思います。 # 2パス方式(約27秒) ./a.out 25.12s user 1.27s system 99% cpu 26.479 total # realloc方式(約15秒) ./a.out 12.73s user 1.46s system 99% cpu 14.263 total -> コピー 1 -> notコピー 636 malloc()されたりrealloc()されたりするヒープメモリは仮想アドレス空間に対応した普通のOSならプロセス毎に別空間だから 他のプロセス(他のサーバ利用者が居る or chrome)があってもヒープメモリの断片化に関係しないはず… さらに、2パス方式の時間が1パス方式の倍になるのはメモリコピーよりもgzip展開を2回もやることが利いているでケテーイ すみません、また疑問が出てきたので相談させてください。 今読み込んだバイナリファイルの解析をしています。 ある一部の8バイトがビッグエンディアン形式で下記になっていて、これが少数点の数値で「2.500000e-04」となるようです。 3e 10 62 4d d2 f1 a9 fc 次に下記URLを参考に調べると今使っているサーバはリトルエンディアンでした。 (CPUはintel) ttps://qiita.com/ryuichi1208/items/31442f9e8a7a7c94aeec このため ビックエンディアン→リトルエンディアン の変換が必要と思うので下記のようにしました。 unsigned char t[8]; double little; for (int i = 0; i < 8; i++) { t[i] = data[7-i]; // バイトオーダーの変換 printf("%02x ", t[i]); } printf("\n"); memcpy(&little, t, 8); printf(" %e\n", little); ---結果--- fc a9 f1 d2 4d 62 10 3e 9.536743e-10 ---------- ちゃんと反転できていますが、結果は期待値「2.500000e-04」ではありませんでした。 >>続きます 次に実際の値を16進数表示したときの確認として下記を実行したところ、結果は以下のようになりました。 int main(){ double data = 2.500000e-04; unsigned char *p; int i; p = (unsigned char *)&data; for (i = 0; i < 8; i++) { printf(" %02X", p[i]); } printf("\n"); printf("%e\n", data); return 1; } ---結果--- FC A9 F1 D2 4D 62 30 3F 2.500000e-04 ---------- 比較すると、最初の6バイトは同じですが最後の2バイトが違いました。 どうやったらバイナリデータに入っている 「3e 10 62 4d d2 f1 a9 fc」 を 「2.500000e-04」 に変換できるかわかりますでしょうか? ビッグエンディアン→リトルエンディアン の変換時にもうひと工夫いるのでしょうか? 2.500000e-04のIEEE754表現は3f30624dd2f1a9fcだぞ 元のバイナリの解釈間違ってんだろ >>426 開発っつーか本当にどうしようもないバグの場合は使う。 遅いって言っても5〜10倍くらいだろ。 2.500000e-04/9.536743e-10≒262144=2^18だからどっかで誰かが18ビットシフトしてないか調べろ あとはそのバイナリ作った野郎に聞け decimal64だったりして サーバのCPUは何? >>440 ,442 データの方が違うんですかね。。 今調べてますがまだわかりません。 >>443 xeonです。 decimal64というのはまだ調べていませんがまたちょっと違う変換になるんですかね? サーバじゃないか もとデータ生成したビッグエンディアンのCPUは何かと 同じサーバで生成されました。 生成したソフトは販売されているものなので問題はないと思うし、 他のソフトで読み取ってもちゃんと 2.5e-04 が出てくるのでデータに問題はなさそうです。 なぜそう読めるのか不思議です。。 Big Endian だし IBM フォーマットとか? >>446 > 生成したソフトは販売されているものなので問題はないと思うし、 なぜそのソフトの名前を書かないの? > 他のソフトで読み取ってもちゃんと 2.5e-04 が出てくるのでデータに問題はなさそうです。 他のソフトって何よ? みんなエスパーじゃないんだから出せる情報は出しなよ >>448 すみません、calmaという大昔のもので今はもう販売されていません。 Calmaでググるとこれがヒットするけどこれかな? https://en.m.wikipedia.org/wiki/Calma これだとかなり古いソフトウェアだから浮動小数点のフォーマットが違うような気がする >>450 数値をバイナリ変換していそうなコードを今見つけました。 ただのビッグエンディアン表記ではなさそうです。 もう少し調べて見ます。 下記で数値からバイナリへの変換ができましたが、何の計算をしているのかさっぱりわかりません…… 逆変換できるのでしょうか? http://codepad.org/oSvOJP1p ---結果--- 変換前の値:0.000250 変換後の値:3e 10 62 4d d2 f1 a9 fc ---------- 浮動小数の正規化っぽいけど ( A < 0.0625 ) || ( A >= 1.0 ) ってのが特殊だと思う 何かそういう事情があったんでしょう 浮動小数の正規化ってのは検索すればわかる 別環境でデコードするだけならたぶん気にしなくて大丈夫 確かに、IBM方式ってやつっぽい 気にしないといかんわw 無駄なこと書いてごめんね >>453-455 ありがとうございます。 今 IBMフォーマット や IBM方式 でググってますが中々それらしいものが出てきません。 かなりマイナーなものなのでしょうか? こんな感じかな?保証はせん uint64_t ibm = 0x3e10624dd2f1a9fcULL if (ibm == 0x0) { return 0.0; } uint64_t sign = ibm >> 63; uint64_t ibmexp = (ibm >> 56) & 0x7f; uint64_t ibmfrac = ibm & 0x00ff'ffff'ffff'ffffULL; if (ibmexp == 0x0) { return std::numeric_limits<double>::quiet_NaN(); } //てきとう uint64_t ieeeexp = (ibmexp - 64) * 4 + 1023; uint64_t ieeefrac = ibmfrac; while(ieeefrac & 0x0100'0000'0000'0000) { ieeefrac <<=1; --ieeeexp; } ieeefrac ^= 0x0100'0000'0000'0000; ieeefrac >>= 1; uint64_t ieee = (sign << 63) | (ieeeexp << 55) | ieeefrac; return *reinterpret_cast<double*>(&ieee); というか指数部のビット長勘違いしてたわ 459は捨ててください恥ずかしい ieee形式は自前で変換せずにdoubleで計算してしまえばいいんでは? frexpとldexpでやっちゃう方が間違えにくくていいのかな エクセス64か 半導体とかで使われるGDSIIの実数がそれなんだよね。 自力で実装したわー。 4bitを16進数一桁として考えてて、指数部は16^xで表現されててケチ表現なし。 じゃあどっかにライブラリでもあるんじゃねえの 探したら出てくるパターンだろこれ >>457-466 コメントやサンプルコードありがとうございます。 こんな複雑なコードになるんですね。。 何やってるのかさっぱりわかりませんが、上記コードを参考に勉強してみます。 しかしエクセス64で検索してもそんなに記事が出てこないですね。 かなりの難易度の予感…… これか? GitHub - enthought/ibm2ieee: Cross-project library for converting IBM-format hexadecimal floating-point to IEEE format binary floating-point. https://github.com/enthought/ibm2ieee 日本語だと情報が少なかったので英語圏も検索してみたところ、下記を見つけました。 ここのexcess64_to_ieee754が変換できるものかもしれません。 これから調べてみます。 ttps://github.com/ulfgri/gdsii-toolbox/blob/de8deaa5972c1449aacc3eea5dda86472ef721a8/Basic/gdsio/convert_float_generic.h >>469 かぶりました。 探していただきありがとうございます。 それも調べてみたいと思います。 >>467 フォーマットは>>458 のリンク先に書いてある 3e 10 62 4d d2 f1 a9 fc の最初の1バイト(3e)は符号ビットと指数 0x3e = (0 0111110) で最初の0は符号(正) 残りの0111110が指数で10進では62 指数は64の時に0と解釈するので62 - 64 = -2 IBM形式では16の指数なので16^(-2) = 1/256 一方仮数部は 1: 1/16 0: 0/256 6: 6/4096 2: 2/65536 : 計 0.064 なので結果は0.064*(1/256) = 0.00025 = 2.5e-4 >>472 わかりやすい解説ありがとうございます。 仮数部の計算がまだいまいち理解できていませんが、符号ビットと指数のところは理解できました。 なんとかプログラム作れそうです。 とても勉強になりました。ありがとうございました。 >>472 仮数部の計算も理解できました。 ネットで浮動小数点を解説している記事を見るとどこも2進数で書かれていましたが、 >>472 はこのスレで会話に出しているバイト単位(16進数)で計算してもらってるんですね。 この辺の話になるとこのスレの趣旨から外れてきているのでこの辺にしておきます。 以上、お礼を兼ねての書き込みでした。ありがとうございました。 bit単位でもいいんだけど元の値との対応が取りにくいから16進の1文字(4bitなのでByteじゃなくてNibbleって言う)単位で計算してる そうでした。4bitでした。 今回バイナリ読みで初めてどっぷり16進数を扱ってますがバイトとニブル?がすぐわからなくなります。。 ttp://www7b.biglobe.ne.jp/~robe/cpphtml/html02/cpp02007.html 上記URLで ・クラスの宣言はヘッダファイルにする ・クラスの実装部はソースファイルにする ・そのクラスを使いたいときはヘッダファイルのみインクルード とありますが、これが一般的なのでしょうか? なるべく基本にのっとった書き方に慣れることで、他の人が書いているプログラムもある程度読めるようになれたら調べるときに助かりそうだと思っています。 だからさ、そういう疑問を持つってことは お前なりに考えた別のやり方があるんだろ? それを書けよ どういう意味や効果があってそうしてるかじゃなくて「一般的」かどうかばかり気にしてるあたりがプログラマ向きの性格じゃなさそう iOSのC++の勉強アプリ何個か試してみたんだけど、未だにC++03時代の事までしか教えてないのね。 でも付属の簡易コンパイラはC++17の機能が使えたりする... 初心者のうちから shared_ptrやunique_ptr を積極的に使うようにしてほしい。 ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.1 2024/04/28 Walang Kapalit ★ | Donguri System Team 5ちゃんねる