C#
iOS
programming
Swift

Swiftで動作している条件式をC#に移植したらハマった話

はじめに

普段はSwiftでiOSアプリ開発してるのですが、Swiftで動作している条件式をC#に移植したら、想定外の動作となりハマりました。。。
以下、実際にハマったコードの再現コードを元に検証していきます。

Swiftのコード

floatValue_6_2_OK.swift
let floatValue: Float = Float("6.2")! // 文字列からFloat値を取得
if floatValue < 6.2 {
    print("\(floatValue)は6.2未満")
} else {
    print("\(floatValue)は6.2以上")
}

このコードの実行結果は6.2は6.2以上となり、想定通りです。

C#のコード

floatValue_6_2_NG.cs
float floatValue = float.Parse("6.2"); // 文字列からFloat値を取得
if (floatValue < 6.2) {
    System.Console.WriteLine($"{floatValue}は6.2未満");
} else {
    System.Console.WriteLine($"{floatValue}は6.2以上");
}

このコードの実行結果は6.2は6.2未満となり、想定通り。。。じゃない!

なんでこうなったのか調べると、条件部分のfloatValue < 6.2の型解決?の動作がSwiftとC#で異っていることが原因でした。

floatValue変数 floatValue
条件部分
6.2 備考
Swift Float Float Float 数値リテラルの型が、変数の型から推論されてFloat型に決まる
C# float double double 変数の型が、数値リテラルのdouble型に合わせてキャストされる

※ double型にキャストした6.2は6.19999980926514なので、C#のコードの実行結果とも合致します。

このため以下のようにコードを修正(条件部分の6.26.2fに修正)して実行すると、

floatValue_6_2_OK.cs
float floatValue = float.Parse("6.2"); // 文字列からFloat値を取得
if (floatValue < 6.2f) {
    System.Console.WriteLine($"{floatValue}は6.2未満");
} else {
    System.Console.WriteLine($"{floatValue}は6.2以上");
}

このコードの実行結果は6.2は6.2以上となり、めでたく想定通りとなりました!!
最終的にC#的には、fの付け忘れということなんだと思いますが、Swiftだとfをつけなきゃいけないっていう認識がないのでハマりました。。。

最後にもうちょっとだけ実験

floatValue_6_2_doubleValue_6_2_cast_result.swift
let floatValue: Float = Float("6.2")!
let doubleValue: Double = Double("6.2")!
print("floatValue=\(floatValue)")
print("doubleValue=\(doubleValue)")
print("floatValue_cast_double=\(Double(floatValue))")
print("doubleValue_cast_float=\(Float(doubleValue))")
floatValue_6_2_doubleValue_6_2_cast_result.cs
float floatValue = float.Parse("6.2");
double doubleValue = double.Parse("6.2");
System.Console.WriteLine($"floatValue={floatValue}");
System.Console.WriteLine($"doubleValue={doubleValue}");
System.Console.WriteLine($"floatValue_cast_double={(double)floatValue}");
System.Console.WriteLine($"doubleValue_cast_float={(float)doubleValue}");

このSwiftとC#のコードの結果は両方とも以下になりました。

実行結果
floatValue=6.2
doubleValue=6.2
floatValue_cast_double=6.19999980926514
doubleValue_cast_float=6.2

精度の高いDoubleへキャストした場合に誤差が発生するみたいです。
なぜなんだろう?