C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

NaNは何に使いますか?

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■90900 / inTopicNo.1)  NaNは何に使いますか?
  
□投稿者/ おばちゃん (1回)-(2019/05/12(Sun) 10:50:10)

分類:[.NET 全般] 

Dim x = 0 / 0
を計算すると
Double.NaNと値が代入されます。

これを判定するために、
If x = Double.NaN then
というIf文が使えるかと思ったのですが、
どうもこれは使えないそうです。

代わりに、
Double.IsNaN関数を使えば調べることができます。

それでは一体Double.NaNという値は何のために存在しているのでしょうか?


引用返信 編集キー/
■90903 / inTopicNo.2)  Re[1]: NaNは何に使いますか?
□投稿者/ キングダム (22回)-(2019/05/12(Sun) 11:51:23)
No90900 (おばちゃん さん) に返信

Double.NaNは未定義を表すためにあると思います
未定義では算術演算が成り立たないので正当な演算ではありませんよという
ことを伝えるのがその目的です

Dim a As Double = Double.NaN
Dim b As Double = 7.0
Dim c As Double = a * b

たとえば上記のような計算を行った場合
NaNが計算に含まれるのでcはNaNになります
結果がNaNになることで算術演算が成り立たない計算を行ったことがわかります

引用返信 編集キー/
■90904 / inTopicNo.3)  Re[1]: NaNは何に使いますか?
□投稿者/ 魔界の仮面弁士 (2158回)-(2019/05/12(Sun) 13:01:16)
2019/05/12(Sun) 14:13:23 編集(投稿者)

No90900 (おばちゃん さん) に返信
> Dim x = 0 / 0
> を計算すると
> Double.NaNと値が代入されます。

他にも、
 Dim a As Double = +1.0 / 0.0 ' = Double.PositiveInfinity
 Dim b As Double = -1.0 / 0.0 ' = Double.NegativeInfinity
 Dim c As Double = a + b
とか
 Dim d As Double = Math.Sqrt(-2.0)
などでも NaN 値が算出されます。



> それでは一体Double.NaNという値は何のために存在しているのでしょうか?

IEEE754 に記される通り、文字通りの非数「Not-a-Number」を表すものですね。
無効な演算や、不正な処理が行われたことを表すための値としても利用されます。

https://ja.wikipedia.org/wiki/IEEE_754
https://ja.wikipedia.org/wiki/NaN


計算途中で非数が含まれた場合、その後の演算結果においても、
例外が発生されることなく NaN として伝播されるようになっています。
NaN 値に対して加減乗除どのような演算結果を行おうとも、
結果は NaN のままです。(このような動作を Quiet NaN と言います)

そのため、最終的な演算結果のみを見ることで演算の失敗を判定でき、
演算式の途中で毎回例外処理を行わなくて済むようになります。
.NET Framework や JavaScript における NaN がこれですね。


なお IEEE 754 においては、Quiet NaN とは別に、Signaling NaN という
ものが定められています。(ビット列としての表現が異なる)
Quiet NaN に対する演算は常に NaN のままとなるのに対し、
Signaling NaN に対する演算は、即時にエラー(あるいは例外)となります。


#If VBC_VER >= 14.0 Then
'倍精度浮動小数点数を、ビット列(64bit幅)で表現した場合、
' 先頭 1 bit は「符号」を表し(0〜1)
' 続く 11 bit は「指数部」を表し(-1022〜1023)
' 残り 52 bit で「仮数部」を表しますが、
'それら【正規化数】とは別に、下記 4 種の特殊値が存在しています。

'====== 浮動小数点数の特殊値 ======
'(1) 指数部と仮数部がすべて 0 の場合、【ゼロ】を意味する。
Dim d0 As Double = BitConverter.ToDouble(BitConverter.GetBytes(&B0__00000000000__0000000000_0000000000_0000000000_0000000000_0000000000_00UL), 0)
Dim d1 As Double = BitConverter.ToDouble(BitConverter.GetBytes(&B1__00000000000__0000000000_0000000000_0000000000_0000000000_0000000000_00UL), 0)

'(2) 指数部がすべて 1 の場合、仮数部がすべて 0 なら【無限大】を意味する。
Dim d2 As Double = BitConverter.ToDouble(BitConverter.GetBytes(&B0__11111111111__0000000000_0000000000_0000000000_0000000000_0000000000_00UL), 0)
Dim d3 As Double = BitConverter.ToDouble(BitConverter.GetBytes(&B1__11111111111__0000000000_0000000000_0000000000_0000000000_0000000000_00UL), 0)

'(3) 指数部がすべて 1 の場合、仮数部が 0 以外なら【非数】いわゆる NaN を意味する。
Dim d4 As Double = BitConverter.ToDouble(BitConverter.GetBytes(&B0__11111111111__1111111111_1111111111_1111111111_1111111111_1111111111_11UL), 0)
Dim d5 As Double = BitConverter.ToDouble(BitConverter.GetBytes(&B1__11111111111__1111111111_1111111111_1111111111_1111111111_1111111111_11UL), 0)

'(4) このほか、指数部がすべて 0、仮数部が 0 以外な【非正規化数】がある。
' 非正規化数は、正規化数同様に有効な値として扱われるが、数値としての精度は落ちる。

'※非数や非正規化数の場合、仮数部のいずれかの bit が 0 以外であればよいので、実際には上記以外の組み合わせも存在する。

'====== 内容を表示してみる ======
'ゼロ
Console.WriteLine($"d0 = {d0}") '通常のゼロ。CType(Nothing, Double) と同義。
Console.WriteLine($"d1 = {d1}") 'マイナスゼロ(あるいは負の無限小)。d0 = d1 とみなされるが、一部演算では異なる結果となる。
' Math.IEEERemainder(+1.0, 1.0) の結果は d0 と等しく
' Math.IEEERemainder(-1.0, 1.0) の結果は d1 と等しい。

'ゼロ除算
Console.WriteLine($"1.0 / d0 = {1.0 / d0}") '非ゼロをゼロで割ると、正の無限大となる。
Console.WriteLine($"1.0 / d1 = {1.0 / d1}") '非ゼロをマイナスゼロで割ると、負の無限大となる。

'無限大
Console.WriteLine($"d2 = {d2}") '正の無限大
Console.WriteLine($"d3 = {d3}") '負の無限大

'非数値
Console.WriteLine($"d3 = {d4}") '正符号範囲の非数値
Console.WriteLine($"d5 = {d5}") '負符号範囲の非数値
#End If
引用返信 編集キー/
■90905 / inTopicNo.4)  Re[1]: NaNは何に使いますか?
□投稿者/ 魔界の仮面弁士 (2159回)-(2019/05/12(Sun) 13:54:29)
2019/05/12(Sun) 13:58:39 編集(投稿者)

No90900 (おばちゃん さん) に返信
> これを判定するために、
> If x = Double.NaN then
> というIf文が使えるかと思ったのですが、
> どうもこれは使えないそうです。

Double 型どうしに対して「=」「<」「>」「<=」「>=」な比較演算を行うと、
一方が NaN だった場合、結果は常に False になりますね。

なので、上記を逆手にとって
 If x <> x Then
とか
 If Not (x <= Double.PositiveInfinity) Then
とか
 If Not (x >= Double.NegativeInfinity) Then
のような比較式を使うことで、NaN であることを
検査することもできるかもしれません。

もちろん上記は無理矢理な判定なので、通常は、
 If Double.IsNaN(x) Then
のように検査するべきですけれども。



ただし、LINQ で Min() を求めるような場合には注意が必要です。
下記のように、NaN が「-∞よりも小さな値」であるかのように処理されてしまうようなので。

 Dim x As Double() = {Double.NegativeInfinity, Double.NaN, Double.PositiveInfinity}
 Dim y As Double = x.Max() '+∞ になる
 Dim z As Double = x.Min() 'NaN になる(-∞ではない)


これは CompareTo メソッドによる比較が行われる場合に、一般的には
 ' b = c の時は a = 0 になる
 ' b > c の時は a = 1 になる
 ' b < c の時は a = -1 になる
 Dim a As Integer = b.CompareTo(c)

のような動作をとるところが、Double.NaN を対象とする場合、

 ( NaN ).CompareTo( NaN ) は 常に 0
 ( NaN ).CompareTo( NaN以外 ) は 常に 1
 ( NaN以外 ).CompareTo( NaN ) は 常に -1

という動作になっていることに由来しています。


Dim dic As New Dictionary(Of String, Double)()
dic.Add("0.0", 0.0)
dic.Add("NaN", Double.NaN)
dic.Add("+∞", Double.PositiveInfinity)
dic.Add("-∞", Double.NegativeInfinity)

For Each y In dic
 For Each x In dic
  Console.WriteLine($"({x.Key}).CompareTo({y.Key}) = {x.Value.CompareTo(y.Value)}")
 Next
 Console.WriteLine()
Next
引用返信 編集キー/
■90906 / inTopicNo.5)  Re[2]: NaNは何に使いますか?
□投稿者/ Hongliang (795回)-(2019/05/12(Sun) 18:30:52)
1. NaNというのは計算結果として出現する
2. 計算結果がNaNかどうかはDouble.NaNとの比較では無くDouble.IsNaNメソッドを使う必要がある
3. ではDouble.NaNという定数の使いどころはあるのか?
という質問のように読み取りました。

個人的には、未初期化を表す値としてDouble型の変数の初期値にDouble.NaNを入れることがたまにあります。
// いまならDouble?型も使えますが、性能的な問題とかDBとのやりとりの問題とかもあって。

他には例えば、WPFではいろいろな値(Widthとか)にDouble型が使われますが、「未定義」(システムが自動的に値を算出する)を意味する値としてNaNが使用されます。
大抵は既定値がNaNですが、後から改めてNaNに設定したい場合もあります。
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ