浮動小数点型変数で遊ぼっ!              花子.

Slides:



Advertisements
Similar presentations
2009/11/10 10 進数と r 進数を相互に変換できる コンピュータのための数を表現できる 2進数の補数を扱える コンピュータにおける負の数の表現を説明で きる コンピュータでの演算方法を説明できる 文字や記号の表現方法を示せる 第7回 今日の目標 § 2.2 数の表現と文字コード.
Advertisements

変数とその種類 変数とは何か? → データ ( 数値 ) を入れておく 箱 1000 変数名とは何か? → 箱に付ける名前 xy 変数名 変数の種類 ( 変数の型 ) → 入れるデータによって箱の種類が異なる int char float double その他たくさん integer (
7章 情報の表現と基礎理論. 数の表現(書き方) 「数」と「数の書き方」をわけて考える 「数の書き方」と,「数そのものの性質」は別のもの 例:13 は素数・・・”13”という書き方とは無関係 ここでは書き方(表現方法)について考える 567.
2.5 プログラムの構成要素 (1)文字セット ① ASCII ( American Standard Code for Interchange ) JIS コードと同じ ② EBCDIC ( Extended Binary Coded Decimal for Information Code ) 1.
復習 配列変数の要素 5は配列の要素数 これらの変数をそれぞれ配列の要素と呼ぶ この数字を配列の添え字,またはインデックスと呼ぶ
復習 配列変数の要素 5は配列の要素数 これらの変数をそれぞれ配列の要素と呼ぶ この数字を配列の添え字,またはインデックスと呼ぶ
基本情報技術概論(第2回) 埼玉大学 理工学研究科 堀山 貴史
配列の宣言 配列要素の初期値 配列の上限 メモリ領域 多次元配列 配列の応用
コンピュータープログラミング(C言語)(2) 1.文字列出力と四則演算 (復習) 2.関数と分割コンパイル
演算、整数型と浮動小数点型 第3回目 [4月27日、H.16(‘04)] 本日のメニュー 1)前回の課題・宿題 2)ファイルサーバの利用
コンピュータープログラミング(C言語)(2) 1.文字列出力と四則演算 (復習) 2.関数と分割コンパイル
課題 1 キーボードから整数値を 1つ読み込み、その値の二乗と三乗を出力するプログラムを作れ。 動作例: % ./a.out
情報・知能工学系 山本一公 プログラミング演習Ⅱ 第7回 データの基本型 情報・知能工学系 山本一公
演算、整数型と浮動小数点型 第3回[平成16年4月27日(火)]:PN04ー03.ppt 今日の内容 1 復習 2 加減・乗除演算子
基礎プログラミングおよび演習 第9回
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
x y 復習 変数とその種類 変数名 数学の場合 未知数 「変数xに‥を代入し‥」 x = 5 x = 1.3
C言語 第2講 生物機能制御学講座 濱田 農学部7号館209室.
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
情報処理Ⅱ 第4回 2007年10月29日(月).
岩村雅一 知能情報工学演習I 第8回(後半第2回) 岩村雅一
情報処理Ⅱ 第2回 2007年10月15日(月).
勉強会その3    2016/5/1 10 8分35秒 データの表現 演算.
プログラミング論 I 2008年5月22日 講義概要 C言語復習
第二回 VB講座 電卓を作ろう.
プログラミング応用 printfと変数.
プログラミング演習I 2003年5月7日(第4回) 木村巌.
プログラミング入門2 第2回 型と演算 条件分岐 篠埜 功.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第8回(C言語第2回) 岩村雅一
第13章 文字の取り扱い方 13.1 文字と文字型関数 13.2 文字列 13.3 文字型配列への文字列の代入
Ibaraki Univ. Dept of Electrical & Electronic Eng.
コンピュータープログラミング(C言語)(2) 1.文字列出力と四則演算 (復習) 2.関数と分割コンパイル
4点FFT設計 ファイヤー和田 知久 琉球大学・工学部・情報工学科 教授
Ibaraki Univ. Dept of Electrical & Electronic Eng.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
2013年度 プログラミングⅡ ~ 計算してみよう ~.
2015年度 プログラミングⅡ ~ 計算してみよう ~.
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
情報処理Ⅱ 第2回:2003年10月14日(火).
先週の復習: CPU が働く仕組み コンピュータの構造 pp 制御装置+演算装置+レジスタ 制御装置がなければ電卓と同様
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
基本情報技術概論(第2回) 埼玉大学 理工学研究科 堀山 貴史
C言語ファミリー C# 高級言語(抽象的) Java オブジェクト指向 C++ C 機械語(原始的)
プログラミング演習I 2003年4月30日(第3回) 木村巌.
地域情報学 C言語プログラミング 第2回 変数・配列、型変換、入力 2017年10月20日
x y 復習 変数とその種類 変数とは何か? →データ(数値)を入れておく箱 変数名 変数名とは何か?
第13章 文字の取り扱い方 13.1 文字と文字型変数 13.2 文字列 13.3 文字型配列への文字列の代入
コンパイラ 2012年10月29日
基本情報技術概論(第13回) 埼玉大学 理工学研究科 堀山 貴史
情報科学 第6回 数値解析(1).
情報処理Ⅱ 第2回 2005年10月14日(金).
情報処理Ⅱ 第2回 2006年10月13日(金).
アルゴリズムとデータ構造1 2009年6月15日
情報処理Ⅱ 2005年10月28日(金).
プログラミング入門2 第2回 型と演算 条件分岐 篠埜 功.
言語プロセッサ 第12日目 平成20年1月9日.
ループだよ!難しいよ! 第5章 while(ループ);.
ca-9. 数の扱い (コンピュータアーキテクチャとプロセッサ)
オブジェクト指向言語論 第二回 知能情報学部 新田直也.
岩村雅一 知能情報工学演習I 第8回(後半第2回) 岩村雅一
岩村雅一 知能情報工学演習I 第8回(C言語第2回) 岩村雅一
アルゴリズムとデータ構造 2010年6月17日
プログラミング演習I 数値計算における計算精度と誤差
情報処理Ⅱ 第2回 2004年10月12日(火).
モバイルプログラミング第2回 C言語の基礎 (1).
printf・scanf・変数・四則演算
復習 いろいろな変数型(2) char 1バイト → 英数字1文字を入れるのにぴったり アスキーコード → 付録 int
C言語講座 四則演算  if ,  switch 制御文.
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
Presentation transcript:

浮動小数点型変数で遊ぼっ!              花子

誰と遊ぶ? それ、どんな子? 双子もいるの? 区別つくかなぁ。。。 どこで遊ぶ? かけっこしよっ! 限界超えて遊ぶぞっ! 朝までオール? でもオールは疲れるよ。。。 違う公園も行こー! さっきのかけっこでズルしたっしょ? 遊び足りない? そろそろお寺の鐘もなるし。。。おかたづけ

float, double, long double C言語には _Complex :複素数型 _Imaginary:虚数型 誰と遊ぶ? C#ちゃん、VBくんとは遊びません。 Visual C++ 2008だけです。 (Professional Edition 90日間お試し版ですが。。。) 浮動小数点型は・・・   float, double, long double   C言語には  _Complex :複素数型            _Imaginary:虚数型 今回は、実数の浮動小数点型について

言語仕様では、相対的な精度だけ決まってます。 float≦double≦long double それ、どんな子? 言語仕様では、相対的な精度だけ決まってます。    float≦double≦long double IEEE 754:浮動小数点演算に関する規格 符号部 指数部 仮数部 単精度 1ビット 8ビット 23ビット 倍精度 11ビット 52ビット 拡張単精度 11ビット以上 31ビット以上 拡張倍精度 15ビット以上 63ビット以上

long double:doubleに変換される(64bit) 16ビット版Visual C++では、 拡張倍精度(80bit) それ、どんな子? Visual C++ 2008では・・・   float :単精度(32bit)   double :倍精度(64bit)   long double:doubleに変換される(64bit) 16ビット版Visual C++では、 拡張倍精度(80bit)

指数部:バイアス(127/1023)を足した値を設定 仮数部:暗黙の1で、精度を1ビット上げる 0.1をfloatにしてみると・・・ それ、どんな子? 指数部:バイアス(127/1023)を足した値を設定 仮数部:暗黙の1で、精度を1ビット上げる 0.1をfloatにしてみると・・・   0.0001100110011001100110011001100・・・ 1.10011001100110011001101×2の-4乗 符号部:0 指数部:-4に127を足して 0111 1011 仮数部:1を取って 100 1100 1100 1100 1100 1101

双子もいるの? 区別つくかなぁ。。。 大丈夫!違う服を着ています。 双子もいるの? 区別つくかなぁ。。。 大丈夫!違う服を着ています。 long doubleはdoubleに変換される Visual C++ 2008では、 精度は同じだけど、型は違う int main(void) long double ll = 0.1L; float ff = ll; ⇒ ‘long double’ から ‘float’ への変換です。 データが失われる可能性があります。 double dd = ll; ⇒ OK }

双子もいるの? 区別つくかなぁ。。。 大丈夫!違う服を着ています。 双子もいるの? 区別つくかなぁ。。。 大丈夫!違う服を着ています。 void calc1(long double a) { printf("long double %f\n", a); } void calc1(double a) { printf("double %f\n", a); int main() calc1(0.1L); ⇒ long double 0.100000 calc1(0.1); ⇒ double 0.100000

FPUという演算装置で浮動小数点の演算を行う どこで遊ぶ? x86系のCPUでは、 FPUという演算装置で浮動小数点の演算を行う FPUには、拡張倍精度(80bit)のレジスタが8本ある        (レジスタ表示で浮動小数点を選択:ST0~ST7)        floatもdoubleも、このレジスタで処理する fld a fld b fstp c ST0 ST1  : ST7 ST0: a ST1  : ST7 ST0: b ST1: a  : ST7 ST0: a ST1  : ST7

こんなコードならfloatもdoubleも同じ。 かけっこしよっ! C = a + b; 【 float 】 fld dword ptr a ;aをST0にpush fadd dword ptr b ; ST0にbを加算 fstp dword ptr c ; ST0をcに設定してpop 【double】 fld qword ptr a ;aをST0にpush fadd qword ptr b ; ST0にbを加算 fstp qword ptr c ; ST0をcに設定してpop こんなコードならfloatもdoubleも同じ。

最適化レベル:/O2 float:1056ms double:233ms かけっこしよっ! float f = 0; for (int i = 0; i < 100000000; ++i) { f += 0.1f; } double d = 0; for (int i = 0; i < 100000000; ++i) { d += 0.1; } 最適化レベル:/O2 float:1056ms double:233ms

doubleは、レジスタのみで処理。 かけっこしよっ! doubleは・・・ fldz ;st0に0をpush fld QWORD PTR __real@3fb999999999999a ;st0に0.1をpush mov eax, 10000000 $LN6@main: sub eax, 1 fadd ST(1), ST(0) ;st1にst0を足す fadd ST(1), ST(0) : jne SHORT $LN6@main doubleは、レジスタのみで処理。

かけっこしよっ! floatは・・・ fldz ;st0に0をpush mov eax, 10000000 fstp DWORD PTR [esp] ;st0をメモリ(f)に設定してpop fld QWORD PTR __real@3fb99999a0000000 ;st0に0.1をpush $LN3@main: fld DWORD PTR [esp] ;st0にfをpush fadd ST(0), ST(1) ;st0にst1を足す fstp DWORD PTR [esp] ;st0をfに設定してpop : ;floatの精度に変換 fld DWORD PTR [esp] fadd ST(0), ST(1) fstp DWORD PTR [esp] jne SHORT $LN3@main fstp ST(0) ;st0をpop

floatは、毎回、計算結果をfloatの精度に変換する。 かけっこしよっ! floatは、毎回、計算結果をfloatの精度に変換する。 浮動小数点のコンパイルオプション /fp:precise・・・デフォルト /fp:fast /fp:fastにすれば、処理も速くなり、 精度も良くなり、 プログラムサイズも小さくなる 計算結果の一貫性を保つため。

かけっこしよっ! printfなし printfあり /fp:precise 99.9990463 /fp:fast 100.0000015 float f = 0; for (int i = 0; i < 1000; ++i) { f += 0.1f; } float f = 0; for (int i = 0; i < 1000; ++i) { f += 0.1f; printf("%.7f", f); } printfなし printfあり /fp:precise 99.9990463 /fp:fast 100.0000015 VC++ 6.0 /O2では/fp:fast相当 VC++ .Net 2003 /Op(浮動小数点の整合性を改善する) VC++ 2005 /fp:precise

doubleは80ビットのレジスタで計算しちゃって良いの? かけっこしよっ! doubleは80ビットのレジスタで計算しちゃって良いの? 全体 仮数部 float 32ビット 23ビット double 64ビット 52ビット レジスタ 80ビット FPUの演算精度 53ビット ⇒ doubleの仮数部と同じ精度なので変換不要 unsigned int control_word; _controlfp_s(&control_word, _PC_64, _MCW_PC); _controlfp_s(&control_word, _PC_53, _MCW_PC); _controlfp_s(&control_word, _PC_24, _MCW_PC);

限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! 限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! double dd = DBL_MAX; dd *= 2.0; dd /= 2.0; float ff = FLT_MAX; ff *= 2.0f; ff /= 2.0f; warning C4756: 定数演算でオーバーフローを起こしました。

限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! 限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! float ff = FLT_MAX; for (int i = 0; i < 12; ++i) { ff *= 2.0f; } ff /= 2.0f; printf("ff = %e\n", ff); double dd = DBL_MAX; for (int i = 0; i < 12; ++i) { dd *= 2.0; } dd /= 2.0; printf(“dd = %e\n”, dd); FPUの演算精度:53ビット  /fp:precise ff = 1.#INF00e+000 dd = 1.797693e+308

限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! 限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! fld DWORD PTR __real@7f7fffff ;st0にFLT_MAXをpush fstp DWORD PTR [esp] ;st0をメモリに設定してpop fld QWORD PTR __real@4000000000000000 ;st0に2.0をpush mov eax, 2 $LN6@main: sub eax, 1 fld DWORD PTR [esp] ;st0にメモリのFLT_MAXをpush fmul ST(0), ST(1) ;st0にst1を掛ける(FLT_MAX×2.0) : ;オーバーフロー!

限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! 限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! fld QWORD PTR __real@7fefffffffffffff ;st0にDBL_MAXをpush fld QWORD PTR __real@4000000000000000 ;st0に2.0をpush mov eax, 2 $LN12@main: sub eax, 1 fmul ST(1), ST(0) ;st1にst0を掛ける(DBL_MAX×2.0) : jne SHORT $LN12@main fstp ST(0) ;st0をpop fld QWORD PTR __real@3fe0000000000000 ;st0に0.5をpush $LN9@main: fmul ST(1), ST(0) ;st1にst0を掛ける jne SHORT $LN9@main

限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! 限界超えて遊ぶぞっ!朝までオール? オーバーフローさせてみよっ! double dd = DBL_MAX; for (int i = 0; i < 12; ++i) { dd *= 2.0; } dd /= 2.0; printf("dd = %e\n", dd); double dd = DBL_MAX; for (int i = 0; i < 12; ++i) { dd *= 2.0; } printf("dd = %e\n", dd); dd /= 2.0; dd = 1.797693e+308 dd = 1.#INF00e+000 /fp:precise ⇒ /fp:strict dd = 1.#INF00e+000 dd = 1.#INF00e+000

0から加算:233ms HUGE_VALから加算: 37742ms でもオールは疲れるよ。。。 double d = 0; for (int i = 0; i < 100000000; ++i) { d += 0.1; } double d = HUGE_VAL; for (int i = 0; i < 100000000; ++i) { d += 0.1; } 0から加算:233ms HUGE_VALから加算: 37742ms

でもオールは疲れるよ。。。 オーバーフローや0割りでは、例外は発生しない。 例外を発生させるには・・・ try/catchでは捕まえれない。 _controlfp_s(&control_word, _MCW_EM & ~_EM_OVERFLOW, _MCW_EM); _controlfp_s(&control_word, _MCW_EM & ~_EM_ZERODIVIDE, _MCW_EM); try/catchでは捕まえれない。   ⇒ ・ __try/__exceptを使う     ・ /EHsc(C++の標準の例外あり)を              /EHa(構造化例外SEHあり)に変更する

SSE:Pentium III SSE2:Pentium4 違う公園も行こー! SSE:Pentium III  SSE2:Pentium4 128bitのレジスタ8本を追加 浮動小数点演算のSIMD処理を行う SSE : 1レジスタに4個の単精度データを格納・演算 SSE2: 1レジスタに2個の倍精度データを格納・演算 レジスタ表示でSSEを選択 :XMM0~XMM7 XMM0:XMM00~XMM03 レジスタ表示でSSE2を選択:XMM0~XMM7 XMM0:XMM0DL,XMM0DH x64の浮動小数点演算はこっち x86でも/arch:SSE /arch:SSE2で使用できる

/fp:preciseではオーバーフローしなかったコード 違う公園も行こー! /fp:preciseではオーバーフローしなかったコード double dd = DBL_MAX; for (int i = 0; i < 12; ++i) { dd *= 2.0; } dd /= 2.0; printf(“dd = %e\n”, dd); /fp:precise /arch:SSE2でコンパイルすると・・・ dd = 1.#INF00e+000

違う公園も行こー! movsd xmm1, QWORD PTR __real@7fefffffffffffff add esp, 20 mov eax, 2 npad 3 $LL12@main: sub eax, 1 mulsd xmm1, xmm0     : jne SHORT $LL12@main

/fp:preciseではめっちゃ遅かったコード 違う公園も行こー! /fp:preciseではめっちゃ遅かったコード double d = 0; for (int i = 0; i < 100000000; ++i) { d += 0.1; } double d = HUGE_VAL; for (int i = 0; i < 100000000; ++i) { d += 0.1; } /fp:precise /arch:SSE2でコンパイルすると・・・ 0から加算:193ms HUGE_VALから加算: 192ms

さっきのかけっこでズルしたっしょ? f = 2097152.0 d = 9999999.9811294507 float f = 0; for (int i = 0; i < 100000000; ++i) { f += 0.1f; } double d = 0; for (int i = 0; i < 100000000; ++i) { d += 0.1; } f = 2097152.0 d = 9999999.9811294507

さっきのかけっこでズルしたっしょ? f f= 9999754.0 float ff = 0; for (int i = 0; i < 10000; ++i) { f = 0; for (int j = 0; j < 10000; ++j) { f += 0.1f; } ff += f; f f= 9999754.0

さっきのかけっこでズルしたっしょ? 20971521回目のループ $LN3@main: fld DWORD PTR [esp] ;st0にfをpush fadd ST(0), ST(1) ;st0にst1を足す fstp DWORD PTR [esp] ;st0をfに設定してpop fld DWORD PTR [esp] ;st0にfをpush ST0 = +1.0000000149011611e-0001 ST0 = +2.0971520000000000e+0006 ST1 = +1.0000000149011611e-0001 ST0 = +2.0971521000000014e+0006 ST1 = +1.0000000149011611e-0001 ST0 = +1.0000000149011611e-0001 ST0 = +2.0971520000000000e+0006 ST1 = +1.0000000149011611e-0001

さっきのかけっこでズルしたっしょ? 浮動小数点数値を加算するときに発生する誤差:情報落ち double d = 0; for (int i = 0; i < 100000000; ++i) { d += 0.1; } d += 1.0E16; double d = 1.0E16; for (int i = 0; i < 100000000; ++i) { d += 0.1; } 後から加算: d = 1.000000001000000e+016 初期値に設定: d = 1.000000000000000e+016

さっきのかけっこでズルしたっしょ? fld QWORD PTR __real@4341c37937e08000 fld QWORD PTR __real@3fb999999999999a add esp, 12 mov eax, 10000000 $LN3@main: sub eax, 1 fadd ST(1), ST(0) : ST0 = +1.0000000000000000e+0016 ST0 = +1.0000000000000000e-0001 ST1 = +1.0000000000000000e+0016 ST0 = +1.0000000000000000e-0001 ST1 = +1.0000000000000000e+0016

遊び足りない? 浮動小数点数値を減算するときに発生する誤差:桁落ち double d1 = 0.1234567; double dd = d1 - d2; d1 = 1.234567016363144e-001 d2 = 1.234565973281860e-001 dd = 1.043081283569336e-007 ・・・有効桁数が小さくなる