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

わんくま同盟

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

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

ツリー一括表示

FloorとCeilingの有効桁数に関して /ぬこ (19/11/02(Sat) 23:18) #92856
Re[1]: FloorとCeilingの有効桁数に関して /shu (19/11/03(Sun) 02:21) #92857
Re[1]: FloorとCeilingの有効桁数に関して /魔界の仮面弁士 (19/11/03(Sun) 10:58) #92859
Re[1]: FloorとCeilingの有効桁数に関して /大谷刑部 (19/11/06(Wed) 17:13) #92906


親記事 / ▼[ 92857 ] ▼[ 92859 ] ▼[ 92906 ]
■92856 / 親階層)  FloorとCeilingの有効桁数に関して
□投稿者/ ぬこ (1回)-(2019/11/02(Sat) 23:18:10)

分類:[.NET 全般] 

FloorとCeilingの有効桁数に関して教えてください。



Dim hh2 As Integer = CInt(Math.Floor(4.0! / 2))
Dim hh3 As Integer = CInt(Math.Floor(6.0! / 2))


Dim hh5 As Integer = CInt(Math.Ceiling(4.0! / 2))
Dim hh6 As Integer = CInt(Math.Ceiling(6.0! / 2))


を実行すると、
hh2とhh5は2
hh3とhh6は3
となります。

SingleやDoubleは

Integerとは異なり、
1.99999992
や3.0000002
のような綺麗な整数ではない値をとるはずです。

そのため、FloorとCeilingの値は一致するはずがないと思うのですが、
なぜ、これらの値は一致するのでしょうか?

有効桁数のようなものが設定されているからなのでしょうか?



[ □ Tree ] 返信 編集キー/

▲[ 92856 ] / 返信無し
■92857 / 1階層)  Re[1]: FloorとCeilingの有効桁数に関して
□投稿者/ shu (1204回)-(2019/11/03(Sun) 02:21:17)
No92856 (ぬこ さん) に返信

一度に計算しないで分解して計算してみると
わかると思いますが、

        Dim hh2 = 4.0! / 2
        Dim hh3 = 6.0! / 2

        Dim fhh2 = Math.Floor(hh2)
        Dim fhh3 = Math.Floor(hh3)
        Dim chh2 = Math.Ceiling(hh2)
        Dim chh3 = Math.Ceiling(hh3)

        Dim ifhh2 = CInt(fhh2)
        Dim ifhh3 = CInt(fhh3)
        Dim ichh2 = CInt(chh2)
        Dim ichh3 = CInt(chh3)

        Console.WriteLine($"hh2 = {hh2}")
        Console.WriteLine($"hh3 = {hh3}")

        Console.WriteLine($"fhh2 = {fhh2}")
        Console.WriteLine($"fhh3 = {fhh3}")
        Console.WriteLine($"chh2 = {chh2}")
        Console.WriteLine($"chh3 = {chh3}")

        Console.WriteLine($"ifhh2 = {ifhh2}")
        Console.WriteLine($"ifhh3 = {ifhh3}")
        Console.WriteLine($"ichh2 = {ichh2}")
        Console.WriteLine($"ichh3 = {ichh3}")


hh2 = 2
hh3 = 3
fhh2 = 2
fhh3 = 3
chh2 = 2
chh3 = 3
ifhh2 = 2
ifhh3 = 3
ichh2 = 2
ichh3 = 3

となります。
4.0! / 2 => 2
6.0! / 2  => 3
なので切り上げようが、切り捨てようが変わりません。
想像通り有効桁数内の計算となるので誤差は発生しないです。
特に2で割ることについては内部でもっている小数表現が
2進数を元にしているので誤差は出にくいかと思います。

[ 親 92856 / □ Tree ] 返信 編集キー/

▲[ 92856 ] / 返信無し
■92859 / 1階層)  Re[1]: FloorとCeilingの有効桁数に関して
□投稿者/ 魔界の仮面弁士 (2454回)-(2019/11/03(Sun) 10:58:27)
2019/11/03(Sun) 11:41:38 編集(投稿者)

No92856 (ぬこ さん) に返信
> SingleやDoubleは
> Integerとは異なり、
> 1.99999992
> や3.0000002
> のような綺麗な整数ではない値をとるはずです。

まず、そこの認識が間違っています。

Single や Double は、2 や 3 という整数値なら、誤差なく格納のできる型です。
小数部がジャスト 0 な整数値なら、Single なら 7 桁までは正確に保持できます。


> Dim hh2 As Integer = CInt(Math.Floor(4.0! / 2))
Single や Double は『二進小数』で管理される型なので、
有効桁数の範囲内に納まるなら、2 で割っても誤差は生じません。

十進数は「10」を基数としているため、「1÷10」や「1÷100」を誤差なく管理できます。
でも「1÷3」は、有限桁数においては近似値表現となりますよね。

三進数は「3」を基数としているため、「1÷3」や「1÷9」を誤差なく管理できます。
二進数は「2」を基数としているため、「1÷2」や「1÷4」を誤差なく管理できますが
「1÷10」は、有限桁数において近似値表現となります。


なお、例示された値についてですが、
1.99999992 は Single や Double では正確に表現できないため、Single なら
1.99999988 という近似値(正確には 1.99999988079071044921875 )になってしまうはずです。

そして Double の場合も、やはりピッタリ同じ値は持てません。
1.99999992 に近い範囲で、Double 型で表現可能な値に丸められます。
1.99999991999999915393004812358 … &H3FFFFFFFEA86711A
1.99999991999999937597465304862 … &H3FFFFFFFEA86711B
1.99999991999999959801925797365 … &H3FFFFFFFEA86711C
1.99999991999999982006386289868 … &H3FFFFFFFEA86711D '☆少し小さい値
1.99999992000000004210846782371 … &H3FFFFFFFEA86711E '★少し大きいが、これがもっとも近い
1.99999992000000026415307274874 … &H3FFFFFFFEA86711F
1.99999992000000048619767767377 … &H3FFFFFFFEA867120
1.99999992000000070824228259880 … &H3FFFFFFFEA867121


> 有効桁数のようなものが設定されているからなのでしょうか?
符号ビットと指数部を取り除いた、「仮数部」のみを有効桁として扱うと
Single 型の有効桁数は 二進小数表現で 23+1 桁。十進数に換算すると約 7.225 桁です。
Double 型の有効桁数は 二進小数表現で 52+1 桁。十進数に換算すると約 15.955 桁です。

Integer 型の有効桁数は 二進整数表現で 32-1桁。十進数に換算すると約 9.332 桁で、
UInteger 型の有効桁数は 二進整数表現で 32桁。十進数に換算すると約 9.633 桁です。


Integer という型で表現できる数値は、隣り合う数が常にジャスト 1 だけの差を持ちますよね。
2 の前は 1 ですし、2 の次は 3 です。整数型ゆえ指数部が無いので、
隣り合う値は常に「2 の 0 乗」すなわち 1 となります。


それに対し、Single や Double においては、隣り合う値の間隔が一定ではありません。
その絶対値が 0 に近いほど細かい差を表現でき、
絶対値が大きくなるほど荒くなり、隣の値の差が広がっていきます。


Single 型で表現可能な 2 の周辺値はこんな感じ。

1.99999964237213134765625 … &H3FFFFFFD
1.99999976158142089843750 … &H3FFFFFFE
1.99999988079071044921875 … &H3FFFFFFF
2.00000000000000000000000 … &H40000000 ★ジャスト2
2.00000023841857910156250 … &H40000001
2.00000047683715820312500 … &H40000002
2.00000071525573730468750 … &H3FFFFFF3


Single 型で表現可能な 200万 の周辺値はこんな感じ。

1999999.625 … &H49F423FD
1999999.750 … &H49F423FE
1999999.875 … &H49F423FF
2000000.000 … &H49F42400 ★ジャスト200万
2000000.125 … &H49F42401
2000000.250 … &H49F42402
2000000.375 … &H49F42403


Single 型で表現可能な 20億 の周辺値はこんな感じ。

1999999616 … &H4EEE6B25
1999999744 … &H4EEE6B26
1999999872 … &H4EEE6B27
2000000000 … &H4EEE6B28 ★ジャスト20億
2000000128 … &H4EEE6B29
2000000256 … &H4EEE6B2A
2000000384 … &H4EEE6B2B


Single 型で表現可能な 2兆 の周辺値はこんな感じ。

1999999729664 … &H53E8D4A3
1999999860736 … &H53E8D4A4
1999999991808 … &H53E8D4A5 ★2兆マイナス8192
2000000122880 … &H53E8D4A6 ☆2兆プラス122880
2000000253952 … &H53E8D4A7
2000000385024 … &H53E8D4A8
[ 親 92856 / □ Tree ] 返信 編集キー/

▲[ 92856 ] / 返信無し
■92906 / 1階層)  Re[1]: FloorとCeilingの有効桁数に関して
□投稿者/ 大谷刑部 (57回)-(2019/11/06(Wed) 17:13:48)
No92856 (ぬこ さん) に返信
> SingleやDoubleは
>
> Integerとは異なり、
> 1.99999992
> や3.0000002
> のような綺麗な整数ではない値をとるはずです。
演算結果が整数でそのような現象が起こることはまずないと思いますけど。
SingleやDoubleの誤差は内部的に2進数管理にしているものを10進数に直して表示している関係上、
2進数で無限小数になる場合と、有限小数でもそれぞれの型の有効桁に収まらない場合に起こるのであって、
計算前、演算結果ともに整数で10進法の実値とずれることはまずないかと思います。
十進法の0.1は当然有限小数ですが、2進法では無限小数になります。
そういう類の話です。

Decimal型は読んで字のごとく直訳すると十進型なので、この手のずれは起こりません。
有効桁内での精度を求めるなら、そもそもSingleやDoubleに非明示型変換が発生する演算や
SingleやDoubleの型の変数を使用した小数ありの計算をするのをそもそもやめるべきです。
質問者さんがどの程度精度を求められる機能を作る必要があるかによります。

> そのため、FloorとCeilingの値は一致するはずがないと思うのですが、
> なぜ、これらの値は一致するのでしょうか?
そもそも例ではずれが発生しないのですから端数処理は実質何もしてない=一致するのは当然。

> 有効桁数のようなものが設定されているからなのでしょうか?
これはさすがにググりましょう。
VBやC#のヘルプで出ている話です。

[ 親 92856 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -