43028


Swiftで以下のコードを実行すると、変数bmiの出力値は25.0となるのにBool文がFalseとなります。  
何が問題でしょうか

[code]

    import UIKit
    
    var weight = 64.0
    var height = 1.6
    var bmi = weight / pow(height, 2)
    
    print(Bool(bmi == 25))
    
[/code]


CC BY-SA 3.0
2018-04-07T05:35:41.827
43025
2018-04-07T06:45:50.967
2018-04-07T06:05:22.770
3060
28058
question
swift
SwiftでBool型が正しい結果を導かない


単なる用語の問題ですが、「Bool文」と言うものはありません。また、Swiftの比較演算子は`Bool`型の値を返すので、比較式を`Bool(...)`で囲むのは意味がありません。

[code]

    print(bmi == 25) //-> false
    
[/code]

* * *

さて、本題ですが、あなたが書かれたような事象は、Swiftに限らず小数を2進浮動小数点数(長ったらしいですが、binary floating point
numberの訳です、単に「浮動小数点」や「浮動小数」と現すこともありますが、10進の場合はまた動作が異なるので「2進」であることは忘れない方がいいでしょう)で表すプログラミング言語では必ず発生する問題です。2進浮動小数点数では、`1.6`のような単純な値さえ正確には表せません。

今のバージョンのSwiftなら`debugPrint()`を使うと、四捨五入による誤差があまり入らないように結果を表示してくれます。

[code]

    print(bmi) //-> 25.0
    debugPrint(bmi) //-> 24.999999999999996
    
[/code]

(`bmi`の中身は2進数で保持されているので、`24.999999999999996`も正確ではないのですが、`25.0`よりは誤差が少なくなっています。)

したがって、通常`Double`や`Float`などの2進浮動小数点数の比較を行う場合、`==`演算子は使わず、許容誤差を決めて、差がその誤差の範囲に収まれば「等しい」と判定します。

[code]

    let epsilon = 0.00000000001
    if abs(bmi - 25) < epsilon {
        print("bmi = 25")
    } else {
        print("bmi ≠ 25")
    }
    //->  bmi = 25
    
[/code]

* * *

10進数型を使えばこのような(10進法で想起される結果との)誤差は生じにくいわけですが:

[code]

    import Foundation
    
    var weight: Decimal = 64.0
    var height: Decimal = Decimal(string: "1.6")! //<- 誤差が入らないように、文字列から直接10進数型に変換する
    var bmi: Decimal = weight / pow(height, 2)
    print(bmi) //-> 25
    debugPrint(bmi) //-> 25
    print(bmi - 25.0) //-> 0
    print(bmi == 25) //-> true
    
[/code]

ただし、10進数型と言っても有効数字は有限なので、思わぬ誤差が入ることもあります。

### コンピュータで小数の比較をする場合には、許容範囲の誤差以内を等しいとする

と言う基本テクニックを覚えておいたほうがいいでしょう。

* * *

興味がおありならば、「2進浮動小数点数 誤差」で検索すれば、山ほど参考になる記事がヒットします。


CC BY-SA 3.0
2018-04-07T06:08:10.183
43028
2018-04-07T06:45:50.967
2018-04-07T06:45:50.967
3060
13972
43025
answer