https://youtu.be/7vezgt_NeDQ
今回からは The Basics
の 浮動小数点数
について眺めていきますけれど、それに先立って前回の最後に話題に登ったプラットフォームの違いによってワードサイズが変わる事例の話を少し補足するところから始めてみますね。どうぞよろしくお願いします。
—————————————————————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #113
00:00 開始 01:56 Apple Watch Series 3 における Int と CGFloat のサイズ 05:10 64 bit の Double と 32 bit の CGFloat との相互変換 06:35 誤差の影響について 07:47 Float と Double はそれぞれ固定サイズ 08:42 丸め誤差 11:08 浮動小数点数のビット表現 16:00 明示的な型変換のありがたみ 17:07 サイズを明記しない整数型を優先する 19:16 浮動小数点数 (IEEE 754) 20:18 Swift における小数表現 21:30 浮動小数点数という存在 23:22 Swift が提供する浮動小数点数型 24:01 拡張倍精度浮動小数点数 24:56 プラットフォームにおける浮動小数点数型の違い 27:23 普段使いの浮動小数点数型 28:42 マイクロコードと浮動小数点数演算 30:53 浮動小数点数型の有効数字 34:44 次回の展望 35:27 浮動小数点数という表現について 37:20 浮動小数点数リテラルと Decimal 37:56 有理数 38:56 無理数 40:45 浮動小数点数が妥当なのかもしれない 43:09 クロージング ——————————————————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #113
はい、では始めます。集まっていただいた皆さん、ありがとうございます。まず、前回の内容を振り返りたいと思います。その後で、スライドを使って説明を進めていきます。
ポイントとして、プラットフォームによってワードサイズが変わるという話に触れました。ワードサイズという言葉は少し曖昧かもしれませんが、要はプラットフォームにより整数型 (Int
) が64ビットになったり32ビットになったりするということです。その中でも、特に注目したのはApple Watch Series 3が現在でも32ビットで動作しているという点です。
前回の勉強会の最後の方では、シミュレーターを使ってInt
型のサイズを確認しました。その時は64ビットでしたが、シミュレーター上のCPUのサイズになっているため、64ビットであるのは当然だと後から気づきました。そのため、実際のApple Watch Series 3を使って確認してみました。
まずは、実際のコードについて説明します。このコードはApple Watch向けに書いたもので、画面上に整数型 (Int
) や浮動小数点型 (Double
と CGFloat
) のサイズを表示させるものです。さらに、最近のSwiftで実装された暗黙の型変換も試しました。SwiftではDouble
型の変数をCGFloat
型の変数に直接代入することができ、その逆も可能です。これを使用して、円周率をDouble
で用意し、CGFloat
に変換し、再びDouble
に戻すという処理を行いました。その結果を表示するためのコードを書きました。
具体的には、以下のようなコードです。
let doubleValue: Double = 3.14159
let cgFloatValue: CGFloat = doubleValue
let backToDoubleValue: Double = cgFloatValue
ここで、各変数の型とその値が正しく変換されているかを確認します。まず、各データ型のサイズを表示します。結果は、Int
型とCGFloat
型が4バイト(32ビット)、Double
型が8バイト(64ビット)です。つまり、CGFloat
型とDouble
型のサイズが異なるにもかかわらず、暗黙の型変換で正常に動作しました。
さらに、各変数の値を確認した結果、X
(Double
型)は3.14から始まり、Y
(CGFloat
型)は有効桁数が切り捨てられ、Z
(再びDouble
型)は元のDouble
型の値とは異なっていました。これは、浮動小数点数の変換によって有効桁数が失われたためです。
この結果から、CGFloat
への変換後に再びDouble
に戻すと値が変わる可能性があるため、浮動小数点数の計算では慎重を期す必要があります。ただし、通常の用途でCGFloat
をDouble
に戻すような処理はあまりないと思いますが、暗黙の型変換に注意が必要です。
次回の勉強会では、さらに深くSwiftの言語仕様や他のプラットフォームにおける差異についても掘り下げていきたいと思います。それでは、今日の内容はここまでにします。ご清聴ありがとうございました。 座標から再計算したり、フレームを再設定したりする際には、注意して操作しないと誤差が含まれる可能性があります。特に、Apple Watchの動作速度が速い場合などに限り、その誤差が目立つことがあります。Appleプラットフォームで活動する場合、座標を操作するにあたって、通常の浮動小数点の有効桁数を超えることはあまりないと思いますが、それでも注意が必要です。
例えば、Double
やFloat
を使っている場合、Double
は64ビットの倍精度浮動小数点数であり、Float
は32ビットの単精度浮動小数点数として決まっています。通常の浮動小数点演算でこれらを使う分には大きな問題はありません。ただし、特定のプラットフォームに依存するCGFloat
などを使用する場合は、注意が必要です。
このように型変換の問題に注意しながら作業を進める必要があります。たとえば、CGFloat
を扱う際も、Double
を利用している場合には明示的に型変換を行う必要があります。同様に、型変換後も適切にDouble
に戻す必要があります。具体的に言うと、以下のようなコードになります:
let x: CGFloat = CGFloat.pi
let doubleX = Double(x)
let floatX = Float(x)
このように明示的な型変換が求められることがあります。
また、ビット単位での操作も非常に重要です。例えば、浮動小数点数をビットパターンに変換して操作する場合、以下のようなコードが使えます:
let x: Double = 1.5
let bitPattern = UInt64(bitPattern: x.bitPattern)
print(String(bitPattern, radix: 2))
このようにして浮動小数点数のビットパターンを取得できます。このビットパターンから、元の数値に戻す操作も必要になります。同様に、Float
からDouble
に変換する場合にもビットパターン操作を考慮する必要があります。このようにして、浮動小数点数の精度や誤差の扱い方について深く理解することで、より正確なプログラムを作成できます。
最後に、浮動小数点数の計算に関する詳しい情報や制約についても考慮することが重要です。このような誤差に関する注意点を理解しておくことが、ソフトウェアの正確性を保つために重要です。 浮動小数点数を用いる場合、「浮動小数点数(Floating Point)」という概念があり、それに基づいた表現方法があります。浮動小数点数は、整数部分と小数部分を持つ数値のことで、そのデータ型には Float
や Double
といった種類があります。特に「IEEE 754」という標準規格に基づいて表現されます。これは、1970年代のコンピュータ黎明期から導入され、現在も使われ続けているもので、非常に巧妙なアルゴリズムで動作します。有効桁数や精度に限界があるものの、モバイルデバイスなどでの計算においてはほとんど気にならない程度の誤差で計算を完了できるという利点があります。
また、Swift言語では、型安全性を重視した設計がされており、「明示的な型変換」が推奨されています。これは、例えば Int32
や Int64
のように固定サイズの整数型を極力避け、プラットフォームに依存しない Int
型や UInt
型を使用することを推奨されているからです。これにより、コードの互換性や保守性が高まり、関数や変数の型推論との相性が良くなるというメリットがあります。
浮動小数点数や整数型の適切な使用は、特にSwiftのような言語では重要です。型安全性を確保しつつ効率的なコードを書くための基本的な知識は、どのプログラミング言語を学ぶ際にも役立つでしょう。
もし、より具体的な浮動小数点数の詳細やIEEE 754の仕様について知りたい場合は、前回の第53回の勉強会やその時のスライドを参照すると良いでしょう。そこには、何ビット目がどのような役割を持つのかなどが詳細に説明されています。 誤差が重なってくることがありますが、ダブルパターで計算すると曖昧な部分を UとYにまとめてくれるという特性があるようです。そういったところがすごいと感じました。本当に昔からこの不動小数点数の仕様が存在しているのはすごいことです。見るたびに感動しますね。
不動小数点数は、実際には固定小数点数が初期に使われていたかもしれませんが、そこからの進化は素晴らしい発明です。Swiftでは標準的に不動小数点数として Double
型と Float
型が提供されています。おそらく今後、浮動小数点数の使用は少なくなるかもしれませんが、まだ完全には消えていません。
インテルCPUではさらに Float80
という80ビットの精度を持つ不動小数点数も使えますが、AppleのCPUではこれがサポートされていません。iPhoneアプリを開発している人には馴染みがないかもしれませんが、操作システム(OS)開発で Float80
に依存していた場合、M1マックス対応で困ることになるかもしれません。
Swiftの標準ライブラリには Float80
が含まれており、インテルCPUで動作します。しかし、X86アーキテクチャに依存するため、スーパーコンピュータやMSXなどの古いシステムでは、異なる不動小数点数の形式が使用されてきました。例えば、MSXでは64ビット不動小数点数が使われていましたが形式が異なっていたため、精度や誤差の計算方式が異なります。
Swiftでは基本的に Double
型と Float
型が用意されていますが、どちらを使用するか迷ったときには Double
型を使うのが一般的です。これは、現代のCPUが Double
型に特化しているためと考えられます。将来的には進化するかもしれませんが、現在のところ Double
型を使うのが最適だと考えられます。
また、過去のアーキテクチャにおいては、X86系がマイクロコードに依存しているため、特定のハードウェアでしか動作しない場合がありました。インテルのマイクロコードが特別に対応しているという状況もあるわけです。
この話をすると、FPU(浮動小数点演算ユニット)が搭載されているかどうかが問題となり、要は外部に任せる不動小数点数の演算があるということですね。 68000CPU、つまりMC68000については、浮動小数点数の演算が80ビットでできるとは限らないという情報があると思います。その部分に関しては、マイクロコードという言葉が頻繁に出てきた気がします。興味のある方はそのあたりを調べてみると面白いかもしれません。ただ、おそらくMC68000でプログラムを書いている人はほとんどいないと思います。もちろん、世の中にはそういったことをしている人も一定数いるかもしれませんが、一般的にはあまり知られていないかもしれません。そのために新しい情報を求める場合は調査する価値があるかもしれません。
さて、ダブル型とフロート型の話に戻りましょう。ちなみに、ダブル型の有効桁数に関する情報がここに書かれています。最低でも15桁とのことですが、この最低という表現が本当のところちょっとわかりにくいですね。フロート型の場合、最低でも6桁とされていますが、これも表現がちぐはぐだと感じました。そこで調べてみると、ダブル型は15.95桁、つまり約16桁表現できるとのことです。一方、フロート型は7.22桁、つまり最大で8桁超えることもあるという情報が得られました。
今のところわかっているのは、ダブル型は16桁、フロート型は8桁表現できるということです。これは、理論的には正しいと思います。一般に、不動小数点数の正確な桁数は正規化の影響を受けるので、細かいところは注意が必要です。ただし、XとYのシンプルな例では、ダブル型は16桁、フロート型は8桁というのが正しいようです。
プログラミングにおいては、精度が必要な場合はダブル型を使用することが推奨されています。Swiftでも同様の推薦がなされており、普段はダブル型を使うのが個人的にもお勧めです。
不動小数点数の話題はまだまだ奥が深いですが、今回はこの辺で終わりにし、新しいセクションに入る準備をしましょう。次回の勉強会で新しいトピックを楽しみにしていてください。残りの時間で不動小数点数についてもう少し掘り下げてみましょう。
Swiftでは長い数値のことを不動小数点数、つまりフローティングポイントと言います。不動小数点数という表現は、加数と指数で数値を表現する方法ですので、非常に合理的です。例えば、Swiftで let number = 10.5
と書いたとき、この 10.5
を小数リテラルではなく、不動小数点数リテラルと呼びます。これは、おそらく型が明示されていないからだと考えられます。型が無い場合、リテラルの扱いとして不動小数点数リテラルとするのは合理的です。 リテラルは型がまだない段階ですね。例えば、これを固定小数点数型に当てはめたとします。不動小数点数リテラルで固定小数点数のインスタンスを取ると変に感じますね。このため、書いたリテラルと実際に使用する数字が違ってしまうという問題があります。
それに対して、現代のコンピューターではSwiftでは不動小数点数でしか小数を表せないということです。割り算の場合も無限に小数点以下が続くことがあるため、同じ問題が発生します。
有理数とは「分数で表現できる数」のことです。有理数であれば無限小数でも問題ありませんが、分数で表現できない数は無理数となり、無限小数になります。例えば、有理数は1/3
のように分子と分母で表現でき、無理数はそのように表現できません。
Wikipediaによれば、有限小数と循環小数のいずれかになるのが有理数です。無理数は循環しない無限小数ですので、その定義を信じると有理数とは分数で表現できる数、無理数とは表現できない数ということになります。
もしかすると、より厳密に小数を表現するリテラルを作ろうとすると、有理数リテラルに合わせる必要があるかもしれません。有理数を二つの整数のペア(分子と分母)で表すようにすれば、一定範囲の小数を正確に表現できます。しかし、これを演算で使うと計算速度の問題が出てきます。例えば、1/3
を使って計算するとき、常に分子と分母を保持する必要があり、これが計算量的に非常に大変です。
そのため、現在のコンピューターは不動小数点数を採用しているようです。不動小数点数リテラルとして表現するのが妥当かもしれません。こうして考えると、フローティングポイントリテラルも不自然ではないかもしれません。
時間になりましたので、本日の勉強会はこれでおしまいにします。このような知識を持ちながら日々プログラミングしていると、新しい発見や面白いことがあるかもしれません。お疲れさまでした。ありがとうございました。