今回ももう少し脱線からのさらなる脱線を続けて、負の整数における剰余
のあたりを詳しく見ていく回にしますね。前回はその具体的事実的なところを眺められたので、今日の回ではそれの考え方や扱う際の判断基準的なところを確認していこうと思います。よろしくお願いしますね。
——————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #258
00:00 開始 00:10 負の剰余を知りたくなった動機 01:22 前回のおさらい 02:37 負の剰余に持つ印象 03:44 剰余の結果が正になって欲しい場面 06:02 負の値で剰余を取ったときに 256 にどう収まる? 10:22 納めたい値の範囲を超えてしまう可能性 12:50 負の値の割り算と余り 16:01 余りが負の値であることは不自然? 19:07 プログラミングの観点で見た、負の値に対する剰余 19:58 言語によって異なる計算結果 20:58 Perl と PHP は違うもの 21:51 負の値に対する剰余演算は注意深くする必要あり 23:41 浮動小数点数に対する剰余計算 27:45 クロージングと次回の展望 ———————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #258
では、進めていきますね。今日は前回に続いて、除余算における負の値の取り扱いについて見ていこうと思います。なぜこれを取り上げるかというと、私自身があまり意識していなかったからです。しかし、こうした曖昧な部分をそのままにしておくのは良くないので、興味が向いたときに調べていこうという意図もあります。また、調べていくうちに意外と厄介な事例が多いことがわかりました。それでは、ゆっくりと見ていくのも悪くないと思っています。
さて、前回はこの除余算がどのように結果を返すかについて、Playgroundを使って実際に試してみました。特に注目したのは -100
を 7
で割った場合の余りで、これが -2
となることでした。この結果について、わかりやすいと感じる人もいれば違和感を覚える人もいるようです。
実際に、世の中でも意見が分かれるポイントのようです。ここでは、どういう理由で意見が分かれるのかについて見ていこうと思います。また、これを知ることで、自分自身の判断基準が明確になればいいなと考えています。
前回見たブログでは、いろいろな答え方が出てきました。例えば、負の整数と正の整数を割った場合の余りが正の整数になってほしい場合もあります。具体的な例として、isMultiple(of:)
メソッドで0と等しいかどうかを判定するケースがあります。この場合、余りが 0
から 6
の範囲で求められることがあります。
通常のユースケースとして、割ることで特定の範囲に制限する場合がよくあります。例えば、ある値を 256
で割ることで8ビット整数 (UInt8
または Int8
) の範囲に制限することがありました。これは古い時代にはよく行われていたもので、最近ではそこまで意識しないかもしれませんが、それでも時折目にすることがあります。
さて、ここまでの話を具体的なコード例で見ていきましょう。例えば、-255
を UInt
にキャストするとどうなるのか試してみたいと思います。
let value = -255
let result = UInt(bitPattern: value)
print(result) // 18446744073709551361
このように、-255
を UInt
にキャストすると非常に大きな正の数になります。これは内部的なビットパターンの扱い方によるものです。
このような負の整数の扱いに関する知識を深めることで、より精度の高いソフトウェア開発が可能になると思います。継続して探求していきましょう。
次のトピックがある場合、それについても続けていきますが、今日はここまでです。質問があれば、ぜひどうぞ。 とりあえずマイナスの 0xFFFFFF
って、これがそうか、えっとマイナスの 0xFFFFFF
。これだけ純粋にビットパターンを取るってことなんですね。えっと、これの乘余を取らないとね。ああ、こんなのは当たり前に用意が1で、残りが1じゃないですね。あれ、ここがよくわかんないな。ビットパターンでえっと、これの256の乘余を取って、ああそうか、ここマイナスのままだとだね。ここでプラスにすると、またこういう風になってくるのか。全然よくわからなくなってきた。何やってるのか、うん、まあいいか。
範囲がマイナスいくだけ、えっとね、だから 0 から 6 という範囲に収めたいところで、これでマイナス2とか出てくると、ちょっと厄介ですね。まあいいや、書かなくなってきた。とりあえず、まだ合うのか合わないのか、うん、まあよくわかんないけど。とりあえず、0 から 6 っていう範囲に押さえることによって、その範囲内でヘイトが使えるから、例えばですね、英語とかでページを入れずに、0 から9個までのときにね、他のインデックスを作るときに、まあインデックスはなんかの値を10で割った乘余ってやって、予想される範囲内のインデックスを無理やり押し込めていくみたいな。まあ、ハッシュテーブルとかそういった発想でやってるんじゃないかな、多分内部的にね。
まあ、そんな感じで乘余を扱ったりするけど、ここでアクセスになってきたところでね、乘余を取ったときに、そもそも x が数の値という状況がそもそも防がれるんでね、基本的には問題なくなるんですけど、うっかり負だったりするとね、aに対してこのマイナス2番目のインデックスにアクセスが発生したりして、よろしくない。ここが普通にね、整数で取れてれば問題ないのにって、そういった話。
さっきのブログは、整数になってほしいっていう例ですね。そういったときこれがね、まさにそう。助数未満の整数に丸めるような配列の話、そう出てきていますね。こういう感じで使うのに、乘余ってね、何か使われがちな印象が少なくても自分は持ってるんで、マイナスの値がね、出てくることはすごく違和感を覚える。簡単に見ると、そんなことないんですけどね。
まあ、そんな感じで、とりあえずそういう事情があって、その上で、ね、他のブログも見ていきたいなと思うんですけど、9の数の割り算と余り、これがね、どんなアプローチかな。ここ数学教員のnao pさんかな、まあその方のブログ、前回のトピックスは…。とりあえず、そう、15割る7イコール2の余り1。懐かしい表記ですね。小学校でこんな書き方やりましたね。
で、いつの日かこういう、割られる数割る数表プラス余り、こういう表記に変わりましたね。普通にね、こういう形で、こういう感じなんですけど、数を割ったときの余り、ここがなかなか面白いテーマらしくて、例えば -34
と 5
で割った余りを考える。そのときに答えが何通りかある。まあ何通りかあるんですけどね、これを満たすのはね、まあその中でも一番近接した範囲内の余りだと、あ、えっと、そう。そういうことですね。一般的にはこれになるのかな、そう、マイナス7の倍数で余りが1、これが一番いいんじゃないかなという感じですね。たとえば、Swiftが結果として出してくる (-34 % 5)
という結果、これは余りが負の数なのでダメという話ですね。 とりあえず、割り算について話します。割り算を行うと、割る数よりも大きい余りを出すことがあるというアイデアがありますが、余りが割る数以上になるのは問題ですね。余りという観点では、これは適切ではありません。
例えば、-34を5で割ると、結果として商が-7で余りが1となります。ただし、マイナスの余りが出ることもあります。余りがマイナスになるのはおかしいと感じるかもしれませんが、解釈次第では納得できます。
例えば、10個のものを4人で分けると、2個ずつ配って2個余ります。しかし、3個ずつ配ると2個足りなくなります。このように、プログラムでも目的によって計算の仕方が変わることがあります。余りが1かマイナス1かという違いも、このような捉え方の違いによるものです。
プログラムや数学の分野で、マイナスの余りが出ることについても考えました。この辺りの違いは、言語や文脈によって異なるかもしれませんね。
他にも、プログラムと負の数に関する話があります。プログラムを書くとき、余りの計算結果を意識することが重要です。ただし、言語によって余りの計算結果が異なることがあります。例えば、C言語系ではマイナスの余りが得られることがありますが、PythonやPerlでは2が得られる場合があります。PHPは余りがマイナス1になるなど、言語ごとに異なる結果が出ることがあります。
こんな風に、計算結果が言語によって異なることがあるので、プログラムを書く際には注意が必要です。 結果がなかなか異なるのは罠ですよね。特にソースコードが同じでも、異なる言語で動かすことはほとんどないですから、あまり気にしないかもしれません。ただ、C言語をやっていた人がPythonでコードを書いたときに、負数が返ってくることを期待していたら、それが原因で厄介なことになることがあります。言語による依存関係や、その言語がその動作を仕様としてサポートしているかどうかをしっかり確認しないと危険です。要は、どの価値観が採用された言語なのか、その言語が仕様としてサポートしているのかを注意しないといけないということです。
特に問題になるのは、除算の際の挙動です。リマインダー、すなわち余りを扱う場合にも注意が必要です。例えば、let x = 1.5
のように小数を使ったときに、整数でない余りも計算できる場合があるかもしれません。これは言語仕様としてサポートされているかどうかを確認する必要があります。
他の言語で整数以外の余りを計算することができるかどうかも気になります。%
演算子がどう扱われるかについても確認すべきです。Swiftでは整数だけで使える仕様のようですが、他の言語でも同様であることが一般的です。かつては可能だったかもしれませんが、仕様変更で廃止されたこともあります。
数学的な計算に近いことをプログラムで行う場合、整数や小数を扱う余りの計算が必要になることがあります。C言語などでも同様のライブラリがあるかもしれませんが、これも確認が必要です。
以上のように、特に余りを扱う際には注意が必要です。Swiftは標準で小数点数の余り計算を関数によってサポートしていますので、その点を意識して利用しましょう。
時間になったので、今回はここまでにします。次回はまたSwiftの広範な機能について取り上げます。興味深い話がたくさん出てくるので、楽しみにしていてください。それではこれで終わりにします。お疲れ様でした。ありがとうございました。