JavaとScalaにおける浮動小数点数の大小比較
浮動小数点数の数値の種類
種類 | Java | Scala |
---|---|---|
正の無限大 | Double.POSITIVE_INFINITY |
Double.NegativeInfinity |
正の数 | 例 1.0 |
例 1.0 |
正のゼロ | 0.0 |
0.0 |
負のゼロ | -0.0 |
-0.0 |
負の数 | 例 -1.0 |
例 -1.0 |
負の無限大 | Double.NEGATIVE_INFINITY |
Double.PositiveInfinity |
NaN | Double.NaN |
Double.NaN |
ゼロが正負の2種類あるのとNaNがあるために数値の大小比較がややこしいです。
2つのゼロ
0.0
と-0.0
はどちらもゼロです。
-0.0
は 0.0 / -1.0
などの計算結果として現れます。
比較演算子ではJavaでもScalaでも2つのゼロは同じ値として振る舞います。
0.0 == -0.0 // true 0.0 > -0.0 // false 0.0 >= -0.0 // false 0.0 != -0.0 // false
JavaのDouble.compare
メソッドでは0.0
と-0.0
は違う値として振る舞います。
Double.compare(0.0, -0.0) // 1 Double.compare(-0.0, 0.0) // -1
ScalaのOrderingのcompare
メソッドでも同様です。
Ordering.Double.IeeeOrdering.compare(0.0, -0.0) // 1 Ordering.Double.IeeeOrdering.compare(-0.0, 0.0) // -1 Ordering.Double.TotalOrdering.compare(0.0, -0.0) // 1 Ordering.Double.TotalOrdering.compare(-0.0, 0.0) // -1
NaN
NaNは非数(not-a-number)というものです。 0.0 / 0.0
などの計算結果として現れます。
!=
以外の比較演算子ではJavaでもScalaでもNaNが含まれるとfalse
を返します。NaN同士の==
も常にfalse
です。!=
は常にtrue
を返します。NaN同士の !=
もtrue
です。
Double.NaN > Double.NaN // false Double.NaN == Double.NaN // false Double.NaN >= Double.NaN // false 0.0 > Double.NaN // false 0.0 < Double.NaN // false 0.0 != Double.NaN // true Double.NaN != Double.NaN // true
JavaのDouble.compare
メソッドではNaNは正の無限大よりも大きな値として振る舞います。
Double.compare(Double.NaN, 0.0) // 1 Double.compare(Double.NaN, Double.POSITIVE_INFINITY) // 1 Double.compare(0.0, Double.NaN) // -1 Double.compare(Double.POSITIVE_INFINITY, Double.NaN) // -1
ScalaのOrderingのcompare
メソッドでも同様です。
Ordering.Double.IeeeOrdering.compare(Double.NaN, 0.0) // 1 Ordering.Double.IeeeOrdering.compare(Double.NaN, Double.PositiveInfinity) // 1 Ordering.Double.IeeeOrdering.compare(0.0, Double.NaN) // -1 Ordering.Double.IeeeOrdering.compare(Double.PositiveInfinity, Double.NaN) // -1 Ordering.Double.TotalOrdering.compare(Double.NaN, 0.0) // 1 Ordering.Double.TotalOrdering.compare(Double.NaN, Double.PositiveInfinity) // 1 Ordering.Double.TotalOrdering.compare(0.0, Double.NaN) // -1 Ordering.Double.TotalOrdering.compare(Double.PositiveInfinity, Double.NaN) // -1
ソート
Java
ソートするときは、正負のゼロは区別されます。NaNは最後に並びます。
var arr = new Double[]{Double.NaN, -1.0, 0.0, -0.0, 1.0}; java.util.Arrays.sort(arr); // => -1.0, -0.0, 0.0, 1.0, NaN
Scala
ScalaでソートしてもJavaと同じソート結果になります。
import Ordering.Double.TotalOrdering
または
import Ordering.Double.IeeeOrdering
どちらかをインポートする必要があります。
インポートしないと
warning: object DeprecatedDoubleOrdering in object Ordering is deprecated (since 2.13.0): There are multiple ways to order Doubles (Ordering.Double.TotalOrdering, Ordering.Double.IeeeOrdering). Specify one by using a local import, assigning an implicit val, or passing it explicitly. See the documentation for details.
という警告が表示されます。
どちらをインポートしてもソートするだけであれば同じです。
ソートではなく min
やmax
メソッドでは2つのOrderingのどちらをインポートするかによって結果が変わってきます。
val seq = List(Double.NaN, -1.0, 0.0, -0.0, 1.0) { import Ordering.Double.IeeeOrdering println(seq.sorted) // List(-1.0, -0.0, 0.0, 1.0, NaN) // Javaと同じ結果 println(seq.min) // NaN <- minは上と結果が違う println(seq.max) // NaN <- maxは上と同じ } { import Ordering.Double.TotalOrdering println(seq.sorted) // List(-1.0, -0.0, 0.0, 1.0, NaN) // Javaと同じ結果 println(seq.min) // -1.0 println(seq.max) // NaN }
sorted
メソッドは内部ではOrderingのcompare
メソッドで比較しています。
min
, max
メソッドは内部ではOrderingのmin
, max
メソッドで比較しています。
Scalaでの2つのOrdering
IeeeOrdering
IeeeOrdering.compare
メソッドはJavaのDouble.compare
を呼び出しており、同じ結果になります。つまり0.0
は-0.0
より大きく、NaNは正の無限大よりも大きいです。
IeeeOrdering
のgt
, gteq
, lt
, lteq
, equiv
メソッドはJavaの比較演算子と同じ結果になります。つまり0.0
と-0.0
は同じ値で、NaNはなにと比較しても常にfalse
、!=
は常にtrue
になります。
IeeeOrdering
のmax
, min
メソッドはjava.lang.Math.max
, java.lang.Math.min
を呼び出しており、どちらかもしくは2つともNaNであれば、NaNを返します。
TotalOrdering
全順序というものです。浮動小数点数のすべての数を並べることができます。IeeeOrdering
のようにNaNや正負のゼロの例外的な挙動がありません。
TotalOrdering.compare
メソッドはJavaのDouble.compare
を呼び出しており、同じ結果になります。つまり0.0
は-0.0
より大きく、NaNは正の無限大よりも大きいです。
TotalOrdering
のgt
, gteq
, lt
, lteq
, equiv
メソッドもJavaのDouble.compare
メソッドと同じ結果になります。つまり0.0
は-0.0
より大きく、NaNは正の無限大よりも大きいです。
TotalOrdering
のmax
, min
メソッドは以下のような定義になっており、0.0
は-0.0
より大きく、NaNは正の無限大よりも大きい扱いです。
def max(x: U, y: U): U = if (gteq(x, y)) x else y def min(x: U, y: U): U = if (lteq(x, y)) x else y
以上。