IT・技術研修ならCTC教育サービス

サイト内検索 企業情報 サイトマップ

研修コース検索

コラム

スーパーエンジニアの独り言

CTC 教育サービス

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  Microsoft Azure  Docker  Kubernetes 

第68回 浮草 (藤江一博) 2017年6月

『加代と清』:

開けっ広げの郵便局の入り口を軽く首を傾げて覗き込み局内には机に向かって何やら仕事の合間に熱心に勉強をしている若いアルバイトの局員が居るのを確かめると、ゆっくりと敷居を跨ぎ玄関を入ってすぐの「電信」と書かれた三角錐の案内表示があるカウンターに肘から下の両腕を載せて上半身を前のめりに体重を掛けて態勢を整えてから、カウンターの台座越しに局員に声を掛ける浴衣姿の清楚な女性です。

加代:「電報用紙ちょうだい。」
 清:「ハイ。」

用紙を渡すと「わたし、ペンでは書けんの。貸して鉛筆。」という加代に鉛筆を貸す清。
鉛筆を舐めて電報の文面を書き始める加代を見つめながら、昨日貴女の芝居を見せて貰ったと告げる清。
加代は「あんた『清さん』っていうんでしょ。」と返答して「どうして知っておられるんです?」と少し驚く清に向かって「ちゃんとわかっとんの、聴いたんやもん。」と素っ気なく関心があるということを伝えた上で気の無い素振りで書き続ける加代。

加代:「はい、お願いします。」
 清:「そこまで きてくたさい」
加代:「違う、『きてください』や。」
 清:「あて名は?」
加代:「あんたや。」

と言って加代は鉛筆を転がす。

 

『浮雲』:

以前のコラム(第31回 浮雲)に浮動小数点数のお話を記載しました。
その内容は、下記の二点に集約出来ます。

1.「コンピュータでの数の表現は二進数である」
2.「コンピュータでの数の表現には桁数がある」

電気信号で稼働する電子計算機であるコンピュータ(パソコン)では有限の数しか表現出来ない事に起因して浮動小数点数には誤差があるということを確認しました。

ですが、筆者は誤差に関連してまた同じような過ちを繰り返す羽目になってしまったのでここに記すことにしましたので、『浮雲』続編となる今回の題名は『浮草』です。

『浮雲』ならぬ『浮草』となる今回は誤差の丸めについ揺らいだ出来事でした。

 

『四捨五入』:

その発端は以下のような現象でした。

C:\Users\kudo-shunsaku>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> num = 4.625
>>> "{:.2f}".format(num)
'4.62'
>>> num = 4.645
>>> "{:.2f}".format(num)
'4.64'
>>> num = 4.655
>>> "{:.2f}".format(num)
'4.66'
>>>

とある集計をするため電卓代わりに Python スクリプトを書いて計算して貰っている時の事です。

集計結果の平均値を算出している所で桁数が多いので小数点第二位で丸めようと思って出力していたのですが、画面に表示された結果がなにやらおかしいのではと勘ぐったのです。
そこで上記の如く確認のために対話モードで再度同等のコード断片を打ち込んでみるとやはり結果が期待通りにはなっていないことが露呈しました。

期待していた結果というのは、「四捨五入」です。

スクリプトで指定した文字列の書式整形機能である str.format() メソッドでは、書式指定文字列で固定小数点数の精度を小数第二位までとしたので第三位を「四捨五入」して表示されると期待していたからです。
確かに以前のバージョン (Python 2.x) では、期待通りになっていたという記憶も確かにありました。
ですが今回の結果を見る限り必ず「四捨五入」ではなく、何故か不定になっているように見えます。

ですから期待通りでは無いこの結果は、実行しているプラットフォームもしくは Python のバージョンから発生している問題かもしれないとも考えたので、Python version 3.5.3 と 3.6.1 の二つを Windows と Linux (Cent OS) のそれぞれで試してみたのですが、すべて同じ結果でした。

ここまで確かめても同じ結果。もしかしてこれは「バグを見つけちゃったのかも?」と勘違いして思い込んだ事、それ自体が間違いでした。

恥を忍んで書きますと、早とちりでおっちょこちょいの筆者は勇み足を踏みまして早速 Pythonのバグトラッカーサイトを探してバグ登録方法を調べてまでちょっと動きが変ですよと報告をしたのでした。

そうするとバグトラッカーサイト登録をしたのも束の間で、すぐさま返信がありました。
返信の主は「マーク (Mark) さん」 と名乗る方でその文面の冒頭に「これは正常な振る舞いです。」と書き出されているのでした。
見た瞬間、いつもの様に「やっちゃったのか?」というのが筆者の本音でした。
返信内容を見ると、無慈悲にばっさりと袈裟切り(けさぎり)で捨て置かれた訳ではなく、親切に教授してくれるために綴っているのが文面から伝わりました。彼の返信には関連情報としてのドキュメントへのリンクと「これはバグではありません」と書き添えられていました。

どうやらもう一度、見直す必要がある様子です。

 

『ラウンド・アンド・ラウンド』:

コミッターが言うのですからたぶん筆者が曲解しているのであろうことは間違いないのでしょうし、何よりも丁寧に情報を提示してくれているのですから素直に聴き入るべきでしょう。

ここでもう一度、浮動小数点数表現には誤差があることは既知でしたので念のため下記を実行してみました。

C:\Users\kudo-shunsaku>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> "{:.20f}".format(4.625)
'4.62500000000000000000'
>>> "{:.20f}".format(4.645)
'4.64499999999999957367'
>>> "{:.20f}".format(4.655)
'4.65500000000000024869'
>>>

上記で確認すると「4.625」はキッチリ「4.625」でした。
少数第三位で「四捨五入」すれば「4.63」である筈ですが結果は「4.62」です。
果たして何が間違っているのでしょうか?
ちょっと躓き(つまづき)掛けたのでマークからの返信にてご指南頂いた内容をもう一度省みることにしました。実はマークからの返信は三通にも及んだのです。筆者が混乱しているのだろうと補足情報をくれたのが二通目、そして二通目の書いた内容のタイポを態々(わざわざ)訂正したのが三通目です。Python 界隈には親切な人が居るのだと実感しました。

そこで提示された関連情報には、組み込み関数である round() についてチェックすべきだと記載がありました。
そうでした。
「丸める」際には round() 関数を使っている筈です。ここから確認するのが道理だと早速試してみました。

C:\Users\kudo-shunsaku>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> round(0.5)
0
>>> round(1.5)
2
>>> round(2.5)
2
>>> round(3.5)
4
>>>

ここではじめて気づいたのです。容疑者は、round() 関数だったのです。
もう一度、今度は小数部を指定した形式で試してみます。

C:\Users\kudo-shunsaku>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> round(4.625, 2)
4.62
>>> round(4.635, 2)
4.63
>>> round(4.645, 2)
4.64
>>> round(4.655, 2)
4.66
>>>

やはり犯人は、round() 関数でした。

では何故、round() 関数はこのような挙動になるのでしょうか?
それを突き止めるには、round() 関数の実装を探ってみるしかなさそうです。
ソースコードを遡って行くには時間が掛かりそうですが、やるしかないみたいです。

その労苦の結果として該当コードを抜粋しておきます。

Python-3.6.1/Objects/floatobject.c
static PyObject *
double_round(double x, int ndigits) {
    double pow1, pow2, y, z;
    if (ndigits >= 0) {
        if (ndigits > 22) {
            /* pow1 and pow2 are each safe from overflow, but
               pow1*pow2 ~= pow(10.0, ndigits) might overflow */
            pow1 = pow(10.0, (double)(ndigits-22));
            pow2 = 1e22;
        }
        else {
            pow1 = pow(10.0, (double)ndigits);
            pow2 = 1.0;
        }
        y = (x*pow1)*pow2;
        /* if y overflows, then rounded value is exactly x */
        if (!Py_IS_FINITE(y))
            return PyFloat_FromDouble(x);
    }
    else {
        pow1 = pow(10.0, (double)-ndigits);
        pow2 = 1.0; /* unused; silences a gcc compiler warning */
        y = x / pow1;
    }
    z = round(y);
    if (fabs(y-z) == 0.5)
        /* halfway between two integers; use round-half-even */
        z = 2.0*round(y/2.0);
    if (ndigits >= 0)
        z = (z / pow2) / pow1;
    else
        z *= pow1;
    /* if computation resulted in overflow, raise OverflowError */
    if (!Py_IS_FINITE(z)) {
        PyErr_SetString(PyExc_OverflowError,
                        "overflow occurred during round");
        return NULL;
    }
    return PyFloat_FromDouble(z);
}

上記が該当部分実装であるソースコードです。
コメントを見ると分かりますが "round-half-even" とうい方式が採用されています。

round() 関数にて実装されている "round-half-even" という方式は、端数が0.5より小さいなら切り捨て、端数が0.5より小さいなら切り上げ、端数がちょうど0.5なら偶数を選ぶというやり方みたいで「偶数丸め(銀行丸め)」というものらしいです。
日常使っている「四捨五入」という丸め方ではないです。これが期待と異なった結果を産んだのです。

改めてオフィシャル・ドキュメントを参照するとちゃんと書いてありました。

「round(number[, ndigits]) と指定した場合には、10のマイナス ndigits 乗の倍数の中で最も近いものに丸められるという方式です。二つの倍数が同じ値なら偶数を選ぶ方に丸められます。例えば、 round(0.5) と round(-0.5) は両方とも 0 に、 round(1.5) は 2 に丸められます。」

先ほど対話モードで実際に試した結果と同じです。ドキュメントにはちゃんと注釈も付記されていて、「浮動小数点数に対する round() の振る舞いは意外なものかもしれません: (中略)これはバグではありません: これはほとんどの小数が浮動小数点数で正確に表せないことの結果です。」

どうやら Python 3.x からは、 round() 関数の処理内容が変更されていたのに気づかなかったのが敗因でした。これは言い訳になりそうですが、確かに Python 2.x の時は round() 関数は「四捨五入」してくれていた筈だからです。
Python 3.x では「偶数丸め(銀行丸め)」"round-half-even" になっていることに注意しましょうという結末です。真犯人は自分(筆者)というオチです。

最初から round() 関数に気づいていれば、そしてちゃんとドキュメント読んでいればと少し後悔しました。ですが、間違ったことからしか学べないのですから恥を書いたことは勉強する機会を得たのだと自分に言い聞かせることにします。

 

『浮草』:

「浮草」"Ukigusa, Floating Weeds" は、「小津安二郎」(Ozu Yasujiro) 監督の手による映画(1959年公開)です。

主演は歌舞伎役者の「二代目 中村鴈治郎」が「嵐駒十郎」を演じます。彼のその強烈なキャラクターは「大映カラー」と称された総天然色で鮮やかに彩られる映画の中にあって、まるでモノクロームの印象が植えつけられます。サイレント映画時代を彷彿とさせるそんな白黒の感触です。独特のトーンで時代を感じさせる台詞も味わい深い中村鴈治郎は、黒澤明 監督の「どん底」"The Lower Depths"(1957年公開)にも出演していました。筆者は未見ですがこの「浮草」は小津安二郎、原作、監督の松竹映画「浮草物語」(1934年公開)のセルフ・リメイク作品という位置づけも兼ねる様子です。

映画本編では季節は夏を迎え晴れた青空の下、青々とした生気ある島の自然の空気に満たされていてそれに調和した節度ある家が山の傾斜に並び、その家並み沿いに設けられた街道や浜辺そして港を行きかう人々の風景が極彩色で鮮やかに健康的に描かれています。
その原色が氾濫する極彩色の中でもはっきりと一等、可憐に華やかに際立っているのが「若尾文子」でした。若尾文子は旅回りの役者「加代」役で出演を果たしています。筆者のみならずとも若尾文子を見るためにこの映画を観ているといっても過言ではないでしょう。
若尾文子演じる加代が蓮っ葉(はすっぱ)でいながら、嫌みのない爽やかな色気を画面一杯に醸し出しています。彼女の若いながらも年齢と時代に相応した艶やかさを醸し出す美麗の彼女を観るだけで価値があるからです。この若尾文子の色香に惑わされるのが初々しい「川口浩」(後に探検隊の初代隊長となる)です。

もう一人の大映の看板女優である「京マチ子」もヒロイン「すみ子」として出演しています。京マチ子は気が強くて嫉妬深く意地悪なところもあるのだけれど、嵐駒十郎を慕う情が深い連れ合い役を見事に演じています。
「京マチ子」と「若尾文子」の二人が出演していることで大映の看板女優の競演が実現しています。これには映画化に際して小津安二郎に因る成り行きみたいな背景があるらしいのですが、もう一人の大映の看板女優であった「山本富士子」のお陰であったみたいです。

他にも、「笠智衆」を筆頭に文学座を結成したカリスマ女優の「杉村春子」や後に川口浩隊長の奥さんとなる愛くるしい「野添ひとみ」も出演して脇を固めていて銀幕のスターだらけの豪華な配役です。総天然色カラーに負けず劣らず物語に鮮やかな彩りを添えています。

 

『若尾文子映画祭』:

コラム冒頭での「加代と清」のやり取りは劇中で若尾文子が川口浩を誘惑しようと画策するものです。このシーンは特に「浮草」と冠した題名通りに若い二人が近づき合う微妙な揺れ具合を自然に描写していて脳幹のネガティブ(フィルム)に鮮明に焼き付きます。
そして何故かこの台詞がリンケージされて蘇ります。

「彼女というのは遥か彼方の女と書く。
 女性は向こう岸の存在だよ、我々にとってはね。
 男と女の間には、海よりも広くて深い川があるってことさ。」

「新世紀エヴァンゲリオン」"Neon Genesis EVANGELION" 第拾八話 「命の選択を」で披露された加持リョウジの有名な台詞がサルベージされて縦横無尽に駆け巡るのです。

何はともあれ、「若尾文子」(Ayako Wakao) の魅力に惹きつけられたというのが「浮草」の鑑賞後の筆者のしがない乾燥の芋(感想)となるのですが、これは筆者に限ったことではないのでしょう。

「若尾文子映画祭 青春」と題した催しが2015年に開催されて銀幕のヒロイン、若尾文子を堪能すべく彼女の出演した主な映画が一挙上映されたのです。人気作である「青空娘」、「妻は告白する」、「女は二度生まれる」に加えて「浮草」や「赤線地帯」などを日本映画の珠玉の数々がスクリーンを賑わせて好評だった様です。

しかも同年には更に上映作品を追加して「若尾文子映画祭 青春」アンコールとして再度催されたほどの盛況ぶりです。

筆者を含めて祭りを見逃した方も多いと想いますが、若尾文子を機会にして彼女の出演作品を振り返ってみるのも良い機会かもしれません。

 

『駅舎』:

「浮草」というタイトルは彼方此方(あちこち)で興行する旅回りの一座という根無し草の様な不安定さと親と子、男と女という間での心の移り変わりの様を表しているのでしょう。

遷り変わる世界の中で現在を生きる私たちも浮草であることは旅回りの一座の彼らとまったく同じです。
「浮草」のラストシーンでは、駅舎での駒十郎とすみ子のぎこちない二人の仲直りのシーンがとても味わい深くてまさに「浮草」を表現してくれます。

すみ子:「親方どこいくの?」
すみ子:「ねぇ、どこいきなはんの?」
すみ子:「親方どこぞあてあんの?」
すみ子:「どこ、どこいきなはんの?」

何度も何度も間をおいて滔々(とうとう)と優しく促す(うながす)すみ子の言葉にノセられて、ボツボツと泣きつこうとする行先を呟く駒十郎。
すかさず「うちも一緒にいこかしら。」と親方と一緒に付いて行きたいと断れないように同意を求めるすみ子。

駒十郎:「のるかそるかや。」
すみ子:「え?」
駒十郎:「もう一旗あげてみよか。」
すみ子:「うん、やりましょ。やろやろ。」
駒十郎:「やってみるか。」
すみ子:「大丈夫や、やろやろ。やりましょ。」

汽車に二人で乗り込み日も暮れた真夏の夜の熱気に包まれた車内の硬い座席に並んで座る二人。
手拭いを頭に載せた嵐駒十郎がすみ子のお酌を受けながら満更(まんざら)でも無い風情。
仲睦まじく寄り添った二人を汽車は儚い希望へと向かって走ります。

二人のやり取りを見ていると描かれる陰鬱な状況を受け入れて悟りを開いた如く、くよくよしても始まらないとばかりに開き直って呑気を装うことで運を呼び込むかの様にあてどなく旅立って行く二人に一縷の望みが見て取れます。

苦境にもめげず、困難にもへこたれず、飄々(ひょうひょう)と生きて行けるのが本当の強さなのでしょう。

水面にプカプカと浮かんでお日様をいっぱいに浴びる浮草の様に。

 

次回をお楽しみに。

 


 

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  Microsoft Azure  Docker  Kubernetes