1 C言語入門 第8週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。) 2 前回の復習 3 訂正 continue 文、break 文によるループの再開と脱出 繰り返し(ループ)の復習 4 ループの再開と脱出 • continue 文 • while, do while, for 文内で使用可能 • 以降の処理を中断してループ末尾から再開する • for文では後処理(第3パラメータ)も実行する • break 文 • while, do while, for, switch 文内で使用可能 • 以降の処理を中断してループを脱出する • switch文の場合はswitch文から脱出する 5 教科書 p.123. 後判定ループ (do while 文) • 真偽値による繰り返し continue break 処理 do { // 条件式 が真の場合の処理 } while (条件式); 真 条件式 偽 do while 文は、ループ内の処理を 最低1回は実行する。 6 教科書 pp.119-122. 前判定ループ (while 文) • 真偽値による繰り返し while (条件式) { // 条件式が真の場合の処理 } 式2 偽 真 continue 処理 break 7 教科書 pp.124-129. 初期化・更新処理付きループ (for 文) • 真偽値による繰り返し 式1 for (式1; 式2; 式3) { // 式2が真の場合の処理 }; 式2 偽 真 continue 前判定ループだが 式1による初期化と 式3による更新処理を ひとまとめにして書ける。 処理 式3 break 8 教科書 pp.123-129. for文とwhile文 (前判定ループ) • 以下のループは等価 • continue時の式3の扱いに注意 for (式1; 式2; 式3) { // 式2が真の場合の処理 }; 式1; while (式2) { // 式2が真の場合の処理 式3; }; 式1 式2 偽 真 for文の continue 処理 while文の continue 式3 break 9 教科書 pp.123-129. for文とwhile文 (前判定ループ) • 以下のループは等価 • continue時の式3の扱いに注意 for (i = 0; i < 10; i++) { // ループ内の処理 }; i = 0; while (i < 10) { //ループ内の処理 i++; }; i = 0 i < 10 偽 真 for文の continue 処理 while文の continue i++ break 10 教科書 pp.119-122. 後判定ループ (do while 文) • continue 後の動作に注目 looptest_dowhile2.c mintty + bash int i = 0, n; $ ./looptest_dowhile2 n = 1 1: 1st continue fprintf(stderr, "n = "); scanf("%d", &n); do { i++; printf("%d: continue\n", i); continue; } while (i < n); もしもcontinue 後に i < n が判定されないなら 無限ループになってしまうが そうはなっていないことに注目 11 教科書 pp.119-122. 後判定ループ (do while 文) • continue, break 後の処理(iの値)に注目 looptest_dowhile.c mintty + bash int i = 0, j = 0, n; $ ./looptest_dowhile n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break fprintf(stderr, "n = "); scanf("%d", &n); do { j++; printf("%d, %d: 1st", i, j); if (j == 2) {printf(" continue\n"); continue;} printf(" 2nd"); if (j == 4) {printf(" break\n"); break;} printf(" 3rd\n"); i++; } while (i < n); mintty + bash $ ./looptest_dowhile n = 0 0, 1: 1st 2nd 3rd do while 文は、 ループ内の処理を 最低1回は実行する。 12 教科書 p.123. 前判定ループ (while 文) • continue, break 後の処理(iの値)に注目 looptest_while.c mintty + bash int i = 0, j = 0, n; $ ./looptest_while n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break fprintf(stderr, "n = "); scanf("%d", &n); while (i < n) { j++; printf("%d, %d: 1st", i, j); if (j == 2) {printf(" continue\n"); continue;} printf(" 2nd"); if (j == 4) {printf(" break\n"); break;} printf(" 3rd\n"); i++; } mintty + bash $ ./looptest_while n = 0 13 教科書 pp.124-129. 初期化・更新処理付きループ (for 文) • continue, break 後の処理(iの値)に注目 looptest_for.c mintty + bash int i = 0, j = 0, n; $ ./looptest_for n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 2, 3: 1st 2nd 3rd 3, 4: 1st 2nd break fprintf(stderr, "n = "); scanf("%d", &n); for (i = 0; i < n; i++) j++; printf("%d, %d: 1st", if (j == 2) {printf(" printf(" 2nd"); if (j == 4) {printf(" printf(" 3rd\n"); } { i, j); continue\n"); continue;} break\n"); break;} mintty + bash $ ./looptest_for n = 0 14 [1] p.281. continue 文 • 以下のループ内に更に小さなループが含ま れない場合の continue は goto contin と同義 for (...) { // ... contin: ; } do { // ... contin: ; } while (...); while (...) { // ... contin: ; } 15 [1] p.281. goto文 • 指定した名札付き文へ移動(ジャンプ)する • 名札(label)は以下のように設定出来る ラベル名: 文 goto 文は 余程理由がない限り使わないこと do while 文相当 while 文相当 for 文相当 loop: ; { // something to do contin: ; } if (expr) goto loop; brk: ; loop: ; if (expr) { // something to do contin: ; goto loop; } brk: ; expr1; loop: ; if (expr2) { // something to do contin: ; expr3; goto loop; } brk: ; 16 暦(カレンダー)の計算 演習 17 月の日数 • 28,29,30,31日の月がある • 2,4,6,9,11月が31に満たない月 • 憶え方:「西向く士」という語呂合わせが有名? 月 1 2 3 4 5 6 7 8 9 10 11 12 平年 31 28 31 30 31 30 31 31 30 31 30 31 閏年 31 29 31 30 31 30 31 31 30 31 30 31 18 紀元前の扱い • 通常の紀年法(1オリジン:one-based) • 天文学の紀年法(0オリジン:zero-based) • 1オリジンだと紀元前の計算が面倒 • 暦の計算では天文学の紀年法である Astronomical year numbering を用いると楽 通常の紀年法 天文学の紀年法 3 BC -2 2 BC -1 1 BC 0 AD 1 1 AD 2 2 19 第4週資料: p.39. ファイルに分離した関数の利用 • 閏年判定は以前作った is_leap_year() 関数を 使おう! is_leap_year_main.c #include <stdio.h> #include <stdlib.h> #include "is_leap_year_func.h" ヘッダファイルを includeして 関数の宣言を取り込む int main() { int year; printf("year = "); scanf("%d", &year); 外部ファイルで定義した 関数を呼び出し printf("%d is%s leap year.\n", year, is_leap_year(year) ? "" : " not"); return EXIT_SUCCESS; } 20 第4週資料: p.40. 分割コンパイル • コンパイルする複数のCのソースファイルを コンパイラに与えれば良い mintty + bash + GNU C $ gcc calendar_monthlen_1.c is_leap_year_func_4_2.c コマンドプロンプト + Borland C++ >bcc32 calendar_monthlen_1.c is_leap_year_func_4_2.c 以下の2ファイルを 予め同じフォルダにコピーしておくこと • is_leap_year_func_4_2.c • is_leap_year_func.h 21 演習: calendar_monthlen_1.c • Astronomical year numberingによるグレゴリ オ暦year年month月における月の日数を表 示せよ(月の日数の後で改行すること) • switch文を用いて場合分けせよ • monthlen に適切な月の日数を代入せよ • calendar_monthlen_tmp.c を元に指定 の位置に作成せよ。 22 ルックアップテーブル • 場合分けが必要な値の変換は配列を用いる とスッキリ書ける場合が多い mdays with switch switch (month) case 2: monthlen = break; case 4: case monthlen = break; case 1: case case 8: case monthlen = break } mdays with array { leap ? 29 : 28; 6: case 9: case 11: 30; int daytab[2][13] = { //0, 1, 2, 3, 中略, 12 { 0, 31, 28, 31, 中略, 31}, { 0, 31, 29, 31, 中略, 31}, }; monthlen = daytab[leap][month]; 3: case 5: case 7: 10: case 12: 31; 23 演習: calendar_monthlen_2.c • Astronomical year numberingによるグレゴリ オ暦year年month月における月の日数を表 示せよ • 配列によるルックアップテーブルを用いよ • calendar_monthlen_1.cのswitch文をルック アップテーブルで置き換える • calendar_monthlen_tmp.c を元に指定 の位置に作成せよ。 24 演習: calendar_yearday_1.c • Astronomical year numberingによるグレゴリオ暦 year年month月day日について年の通算日を表 示せよ • 年の通算日の数え方は1月1日を1日目、12月 31日を356または366日目とする • calendar_monthlen_2.cで用いたルックアッ プテーブルを用いてfor文で積算せよ • calendar_monthlen_tmp.c を元に指定の位 置に作成せよ。月の日数はyeardayに代入せよ。 25 演習: calendar_yearday_2.c • Astronomical year numberingによるグレゴリオ暦 year年month月day日について年の通算日を表 示せよ • 年の通算日の数え方は1月1日を1日目、12月 31日を356または366日目とする • ルックアップテーブルを予め積算しておくことで ループを用いずに計算せよ • calendar_monthlen_tmp.c を元に指定の位 置に作成せよ月の日数はyeardayに代入せよ。 26 C言語における丸め • 丸めとは? • 小数点以下を処理して整数にする処理 • • • • 切り上げ 切り捨て 四捨五入 等々 27 [1] p.315. ceil 関数 • double ceil(double x); • 天井関数(切り上げ): 𝑥 • 引数: • x: 浮動小数点数 • 戻り値: • x より小さくない最小の整数を返す • 例: -0.5→ 0、0.5 → 1 • 要: math.h 28 [1] p.315. floor 関数 • double floor(double x); • 底関数(切り捨て): 𝑥 • 引数: • x: 浮動小数点数 • 戻り値: • x より大きくない最大の整数を返す • 例: -0.5→ -1、0.5 → 0 • 要: math.h 29 int 型へのキャスト • (int) x; • 小数点以下が取り除かれる • 元の値: • x: 浮動小数点数 • 変換後の値: • 0 への丸めとして働く • 例: -0.5→ 0、0.5 → 0 30 各丸めイメージ • ±での動作に注意 floor(x) -1 ceil(x) (int) x floor(x) (int) x 0 ceil(x) 1 31 round 関数 • double round(double x); • 四捨五入関数 • 引数: • x: 浮動小数点数 • 戻り値: • x を四捨五入した整数を返す • 0.5は0から遠い方に丸める • 要: math.h (C99) 32 C89 での四捨五入 • C89 には round 関数がない • 0.5 を足してから底関数を取れば良い • 負の数の0.5をどちら方向に丸めるかには注意 0.5を正の無限大方向に丸める四捨五入 x = floor(x + 0.5); 0.5を0から遠い方に丸める四捨五入(C99 互換) x = (int) (x + (x < 0 ? -0.5 : 0.5)); 33 参考 IEEE丸め • IEEE754 では以下の4つが定義されている • 最近接偶数への丸め (RN: round to the nearest even) • round 関数?ではない! • 0への丸め (RZ: rounding toward zero) • int 型へのキャスト • 正の無限大への丸め (RP: rounding toward plus infinity) • ceil 関数 • 負の無限大への丸め (RM: rounding toward minus infinity) • floor 関数 34 参考 最近接偶数への丸め • 0.5を丸める際、最近接偶数へ丸める • 例: 0.5 → 0.0、1.5 → 2.0 • C99 では以下の方法を用いる • fesetround 関数で丸めモードを設定 • nearbyint 関数で丸める 35 先発グレゴリオ暦 • グレゴリオ暦のルールですべての年月日を表 現する • 実際にはグレゴリオ暦は1582年から施行された • それ以前からグレゴリオ暦が施行されていたと仮 定した仮想の暦 36 先発グレゴリオ暦と閏日の回数 • n年1月1日以前に2月29日(閏日)は何回 あったか?(便宜上0年1月1日時点で0回と する) 年 0 1 2 3 4 5 6 7 8 9 10 回数 0 1 1 1 1 2 2 2 2 3 3 年 98 99 100 101 102 103 104 105 106 107 108 回数 25 25 25 25 25 25 25 26 26 26 26 年 398 399 400 401 402 403 404 405 406 407 408 回数 97 97 97 98 98 98 98 99 99 99 99 37 先発グレゴリオ暦と閏日の回数 n/4.0 と比べてみる n 0 1 2 3 4 5 6 7 8 9 10 n/4.0 0.00 0.25 0.50 0.75 1.00 1.25 1.50 1.75 2.00 2.25 2.50 年 0 1 2 3 4 5 6 7 8 9 10 回数 0 1 1 1 1 2 2 2 2 3 3 𝑛/4.0 を取れば良い事が分かる 𝑥 は天井関数で𝑥より小さくない最小の整数 100,400のルールも同様に考えれば答えは 𝑦/4.0 − 𝑦/100.0 + 𝑦/400.0 38 演習: calendar_rdleap.c • Astronomical year numberingによるグレゴリ オ暦year年1月1日以前に2月29日(閏日)は 何回あったか表示せよ • 便宜上0年1月1日時点で0回とする • calendar_rdleap_tmp.c を元に指定箇所 に作成せよ。変数は用意されている物以外使 わない事。計算結果はrdleepに代入せよ。 39 Fixed Day Numbers (RD: Rata Die) 先発グレゴリオ暦1年1月1日子午を1日目とし た通算日 𝑅𝐷 = 365𝑦 + 𝑦/4 − 𝑦/100 + 𝑦/400 −366 + 𝑦𝑒𝑎𝑟𝑑𝑎𝑦 𝑥 は天井関数(𝑥より小さくない最小の整数) C言語では ceil() 関数 (要: math.h) Edward M. Reingold and Nachum Dershowitz. Calendrical Calculations: The Millenium Edition Cambridge University Press; 2nd edition (2001). ISBN 978-0521777520 pp.11-18. 40 演習: calendar_rd.c • Astronomical year numberingによるグレゴリ オ暦year年month月day日はRata Dieの何 日目であるか表示せよ • calendar_rd_tmp.cを元に指定箇所に作 成せよ。変数は用意されている物以外使わな い事。計算結果はrdに代入せよ。 41 演習: calendar_wday.c • Astronomical year numberingによるグレゴリ オ暦year年month月day日が何曜日か表示 せよ • 日月火水木金土は英語表記の頭文字を用い てSu,Mo,Tu,We,Th,Fr,Saと表示せよ • calendar_wday_tmp.c を元に指定箇所に作 成せよ。変数は用意されている物以外使わな い事。日月火水木金土に対して0,1,2,3,4,5,6 の値をwday代入せよ。 42 演習: calendar_wday.c • ヒント: • RD を7で割った値はどうなるか? • 例えば今日の日付で確認してみる 43 演習: calendar_1.c • 1からnまでの整数を横方向に表示せよ • 数値は幅3桁右詰で表示し、前後に空白を入 れないこと("%3d"を使え) • 最後は改行すること • calendar_1_tmp.cを元に指定位置に作成 せよ。変数は用意されている物以外使わない 事。 n=19 の例 $ ./calendar_1 n = 19 1 2 3 4 5 $ 6 7 8 9 10 11 12 13 14 15 16 17 18 19 44 演習: calendar_2.c • calendar_1.c を改造し7日毎に改行せよ • calendar_2_tmp.cを元に指定位置に作成 せよ。変数は用意されている物以外使わない 事。 n=31 の例 $ ./calendar_2 n = 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ 45 演習: calendar_2.c • ヒント: • calendar_1.cから、使える変数が1つ増えてい ます(変数w)。この変数をどう使うかがポイントで す。 • w をカウンターとして利用して、dayを7回表示す る毎に条件分岐して、改行させれば目的を達せ 出来るはずです。 46 演習: calendar_3.c • calendar_2.c を以下のように改造せよ • 最初の行の表示を変数wdayに格納された数値 の数だけ3ケタの空白(" ")を表示した後、1 日目を表示し始めるようにせよ • その際、1行目は7-wday日目で改行し、以降7 日毎に改行せよ(例参照) n=31, wday=2 の例 • calendar_3_tmp.cを元に $ ./calendar_3 指定位置に作成せよ。 n = 31 変数は用意されている物以外 wday = 21 2 3 4 5 6 7 8 9 10 11 12 使わない事。 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ 47 演習: calendar_3.c • ヒント: • " "(空白3つ)を表示するループをwday回実 行した後に、calendar_2.c で作成した処理を 実行すれば良いでしょう。 • " "を表示するループでwをforループのカウ ンタとして用いると、calendar_2.cの処理と上 手く整合性が取れるでしょう。 48 演習: calendar_4.c • Astronomical year numberingによるグレゴリ オ暦year年month月についてカレンダーを表 示せよ • 週の始まりは日曜日とする • calendar_4_tmp.cを元に 例 指定位置に作成せよ。 $ ./calendar_4 = 2014 変数は用意されている物以外 year month = 6 1 2 3 4 5 6 7 使わない事。 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 $ 49 演習: calendar_4.c • ヒント: • 以下の結果を利用すれば出来るはずです。 • calendar_monthlen_2.c • calendar_wday.c • calendar_3.c • calendar_3.cでは日付を表示するループで上 限の条件指定にnを用いていましたが、 calendar_4.cでは使えなくなっています。代わ りにdaytabから月の日付を取って来ましょう。 50 ユリウス日(JD: Julian Day) • ユリウス暦の紀元前4713年1月1日正午を0 日とした経過日数 • 各種暦の変換に用いられる • Wikipedia • ユリウス通日 • Julian day 51 ユリウス日0日 • ユリウス暦だと • 紀元前4713年1月1日正午 • -4712年1月1日正午 • グレゴリオ暦だと • 紀元前4714年11月24日正午 • -4713年11月24日正午 52 ユリウス暦とグレゴリオ暦 • ユリウス暦 • 閏年は4年に1回 • グレゴリオ暦 • 閏年は400年に97回 • Tropical year(太陽年、回帰年) 𝑡𝑎 = 365.2421896698 FITS Time WCS Draft −6.15359 × 10−6 𝑇 Ver.0.90 McCarthy & Seidelmann, −7.29 × 10−10 𝑇 2 2009, p. 18.; Laskar, 1986 −10 3 +2.64 × 10 𝑇 • 𝑇 はユリウス世紀数 J2000.0(2000年1月1日)起点のユリウス世紀(36525日) 53 ユリウス暦 • 紀元前45年1月1日から運用開始 • 閏年は4年に1回 • 8 BC まで 3 年に 1 回の閏年(運用の誤り) • 6 BC から AD 7 までは閏年を停止(補正のため) • AD 8 以降は 4 年に 1 回の閏年 • 以下も参考になる • 国立天文台 / 暦計算室 • トピックス / 1月1日あれこれ • 暦Wiki / 曜日の始まり 結局この辺りは古い話なの で実際の暦がどうなってい たかは良く分からない。 実際、フリーゲルの公式で は運用ミスは考慮されてい ないし、あまり深入りしない 方が良さそう 54 ローマ・カトリック教会 ユリウス暦からグレゴリオ暦へ ユリウス暦1582年10月4日(木曜日) ↓翌日 グレゴリオ暦1582年10月15日(金曜日) グレゴリオ暦では閏年が4年に1回だったため春 分が3月21日前後から10日程度ずれてしまって いたのを修正することが目的 ただし宗派により実施時期が異なる 55 日本 天保暦からグレゴリオ暦へ 明治5年(1872年)12月2日 ↓翌日 明治6年(1873年)1月1日 政府財政が逼迫する中、明治6年は閏月があるた め、公務員の月給を13回支払う必要があった。 欧 米と共通の暦を採用するついでに、 12月を2日で 切り上げることで12月と閏月、計2ヶ月分の月給を カットも狙った。 ただし当時の日本は西暦ではく皇紀(西暦+660年) ゼロ戦 = 零式艦上戦闘機 = 皇紀2600年式艦上戦闘機 = 西暦1940年式艦上戦闘機 56 修正ユリウス日(MJD: Modified JD) • 1858年11月17日子午を0日とした経過日数 • MJD = JD - 2400000.5 57 フリーゲルの公式 int型で計算すると 底関数を取る前に 整数になってしまう ので注意 修正ユリウス日を求める公式 1,2月を前年の13,14月として扱うと グレゴリオ暦𝑦年𝑚月𝑑日に対して 𝑀𝐽𝐷 = 365.25𝑦 + 𝑦/400.0 − 𝑦/100.0 + 30.59 𝑚 − 2 + 𝑑 − 678912 ユリウス暦𝑦年𝑚月𝑑日に対して 𝑀𝐽𝐷 = 365.25𝑦 + 30.59 𝑚 − 2 + 𝑑 − 678914 ⌊𝑥⌋は底関数(𝑥を超えない最大の整数) C言語では floor() 関数 (要: math.h) 58 演習: calendar_mjd.c • グレゴリオ暦y年m月d日の修正ユリウス日を フリーゲルの公式を用いて計算し表示せよ • calendar_mjd_tmp.c を元に指定の位置 に作成せよ。変数は用意されている物以外使 わない事。 59 参考文献 • [1] B.W.カーニハン/D.M.リッチー著 石田晴久 訳、プログラミング言語C 第2版 ANSI 規格準 拠、共立出版(1989)
© Copyright 2024 ExpyDoc