エラーの対処の仕方がわかりません

タグの編集
投稿者 イヨ  (社会人) 投稿日時 2020/10/5 17:24:40
いつもお世話になっております
下記コードは前回質問したときのコードです。
ちょっとアレンジや参考にして遊んでみたところそのままでは動くのですが
http://rucio.cloudapp.net/ThreadDetail.aspx?ThreadId=30546

★が付いているところで(自分が変えたところ)
System.OverflowException: '算術演算の結果オーバーフローが発生しました。'
と、出てきます。調べた結果型が合わないからでるらしく型を変更してみましたり、
エラー対処してみましたがいい方法が思いつかなかったです。。

ボタンをクリックした二回目以降factor変数が0のままでした。
こういう時どうすればいいでしょうか?
宜しくお願いいたしますmom


(△マークのところを素直に60や40などにしておくと出ないのですが。。。)

Private Sub PictureBox2(sender As Object, e As EventArgs) Handles PictureBox2.Click
  Dim sp As System.Drawing.Point = System.Windows.Forms.Cursor.Position
    Dim cp As System.Drawing.Point = Me.PointToClient(sp)
    Dim x As Integer = cp.X
    Dim y As Integer = cp.Y
        Dim newX As Integer = RoundMultiple(x,60/100 * Form4.TrackBar2.Value)△
        Dim newY As Integer = RoundMultiple(y, 40/100 * Form4.TrackBar2.Value)

    省略(↑上記座標を使いGraphicsで円とか書いておえかきしています)

        Exit Sub




 Private Function RoundMultiple(value As Integer, factor As Integer) As Integer
        Dim rate As Double = value / factor
        Dim value以下で最も大きい倍数 As Integer = CInt(Math.Floor(rate) * factor)★
        Dim value以上で最も小さい倍数 As Integer = CInt(Math.Ceiling(rate) * factor)
            Dim 差1 As Integer = value - value以下で最も大きい倍数
            Dim 差2 As Integer = value以上で最も小さい倍数 - value
            If 差1 < 差2 Then
                Return value以下で最も大きい倍数
            Else
                Return value以上で最も小さい倍数
            End If


投稿者 るきお  (社会人) 投稿日時 2020/10/5 18:53:50
CInt で Integer に変換することが前提になっており、計算の過程または結果でIntegerの範囲を超える場合は、OverflowExceptionになります。
Integerの最大値は約21億です。
画面に描画するための座標としては21億もあれば十分間に合いますので、そのような計算が必要になる状況の方に何か問題があると思います。
たとえば、画面におさまらないかなり巨大な座標を計算しようとしているなどです。

>ボタンをクリックした二回目以降factor変数が0のままでした。
>こういう時どうすればいいでしょうか?
投稿されたプログラムにボタンのプログラムがなく、また、他の部分もそのままで動作しないため、何が起こっているのかわかりませんでした。
問題のボタンの部分も含めてコピー&貼り付けすればすぐ動く状態のプログラムを投稿していただければ何かアドバイスできるかもしれません。

参考に挙げられている
http://rucio.cloudapp.net/ThreadDetail.aspx?ThreadId=30546
に掲載されているプログラムはコピー&貼り付けでそのまま動作しますので、そのような感じで投稿いただくのが良いです。
投稿者 イヨ  (社会人) 投稿日時 2020/10/6 08:48:47
るきお様
お世話になっております。

自分ではまだまともに書けないのでるきお様が書いてくれたコードを動かして楽しく遊んでいます
ありがとうございますmom


>投稿されたプログラムにボタンのプログラムがなく、また、他の部分もそのままで動作しないため、何>が起こっているのかわかりませんでした。
>問題のボタンの部分も含めてコピー&貼り付けすればすぐ動く状態のプログラムを投稿していただけ>れば何かアドバイスできるかもしれません

すいません。
下記にコードを貼り付けました。
こちらのコードはForm1にPictureBox1とTrackBar1が存在します。
宜しくお願いいたします。


Public Class Form1
    Private Sub PictureBox1_Click(sender As Object, e As EventArgs) Handles PictureBox1.Click
        '▼マウスの座標を取得して学校関数でYだけ反転   円を書く 
        Dim sp As System.Drawing.Point = System.Windows.Forms.Cursor.Position
        Dim cp As System.Drawing.Point = Me.PointToClient(sp)
        Dim schoolP As System.Drawing.Point = Me.PointToSchool(cp)
        Dim x As Integer = schoolP.X
        Dim y As Integer = schoolP.Y

        Dim newX As Integer = RoundMultiple(x, 60 / 100 * TrackBar1.Value)
        Dim newY As Integer = RoundMultiple(y, 40 / 100 * TrackBar1.Value)

        'MsgBox(newX & "," & newY & " の位置にマウスカーソルを移動します。") 
        Dim g As Graphics = PictureBox1.CreateGraphics
        g.TranslateTransform(0, Me.ClientSize.Height)
        g.ScaleTransform(1, -1)
        g.DrawEllipse(Pens.Blue, newX - 15, newY - 15, 30, 30)
        g.Dispose()
    End Sub
    '----------------------------------------------------- 
    '▼学校関数で反転させる 
    Private Function PointToSchool(p As Point) As Point
        Dim x As Integer = p.X
        Dim y As Integer = Me.ClientSize.Height - p.Y
        Return New Point(x, y)
    End Function
    '----------------------------------------------------- 
    '▼クリックした座標は指定した近い倍数になる 
    Private Function RoundMultiple(value As Integer, factor As IntegerAs Integer
        Dim rate As Double = value / factor
        'MsgBox(factor) 
        Dim value以下で最も大きい倍数 As Integer = CInt(Math.Floor(rate) * factor)
        Dim value以上で最も小さい倍数 As Integer = CInt(Math.Ceiling(rate) * factor)

        Dim 差1 As Integer = value - value以下で最も大きい倍数
        Dim 差2 As Integer = value以上で最も小さい倍数 - value
        If 差1 < 差2 Then
            Return value以下で最も大きい倍数
        Else
            Return value以上で最も小さい倍数
        End If
    End Function

End Class

投稿者 イヨ  (社会人) 投稿日時 2020/10/6 09:23:03
まちがえて解決チェックすけちゃいました・・
投稿者 るきお  (社会人) 投稿日時 2020/10/6 18:21:13
プログラムありがとうございます。

RoundMultiple関数の第2引数 factor が 0 の場合、
Dim rate As Double = value / factor のところで、 0 で割り算していることになります。
結果として rate は無限になります。

そして、次の行
CInt(Math.Floor(rate) * factor)

カッコの中は無限の小数点以下を切り捨てて 0 を掛け算します。無限×0 となり、もう評価不能で、この結果は数値ではない特別な値 NaN になります。

そうすると数値ではないものを CInt で整数に変換しようとしていることになるのでここれでOverflowExceptionの例外が発生します。

簡略化すると次のプログラムのような状況です。
Dim mugen As Double = 1 / 0
Dim nan As Double = mugen * 0
Dim i As Integer = CInt(nan)    '←ここで OverflowException 



RoundMultiple関数の第2引数 factor が どのような場合 0 になるか。
2回目の呼び出し時にこの値は次の計算によって求められています。
40 / 100 * TrackBar1.Value

TrackBar1.Value が 小さいときこの計算結果は 1 以下の小数になります。
しかし、RoundMultiple関数の第2引数 factor は Integer 、つまり整数で待ち構えているので、小数をそのまま渡すことはできません。
そこで、呼び出し時に四捨五入のような丸めが行われ、0.5以下の場合、0 にされてしまいます。

具体的には TrackBar1.Valueが 0 または 1 のときにこれを実行すると factor は 0 になり、OverflowExceptionが発生するというわけです。

TrackBar1.Value が 1 のとき、 40 / 100 * 1 = 0.4 → 丸められて 0 が factor に渡される
TrackBar1.Value が 2 のとき、 40 / 100 * 2 = 0.8 → 丸められて 1 が factor に渡される


このような暗黙の計算は、バグの原因となりやすいので、暗黙の計算に気が付ける Option Strinct On を好むプログラマーも多くいます。 
Option Strict On を使用するには、ファイルの先頭に Option Strict On と記述します。
(プロジェクトレベルの設定ですべてのファイルで On にすることもできます。)

Option Strict On にすると、イヨさんのプログラムの
40 / 100 * TrackBar1.Value
の行は赤い波線が表示されて、何か良くないことがあるということがわかります。
投稿者 イヨ  (社会人) 投稿日時 2020/10/7 14:34:20
るきお様
お世話になっております。

>RoundMultiple関数の第2引数 factor が 0 の場合、
>Dim rate As Double = value / factor のところで、 0 で割り算していることになります。
>結果として rate は無限になります。

わかりやすいようにコードと説明してくださり有難うございます。
理解できました自分0で割り算してたの気づかず、違うエラーが出たところばかり気にしてました。。。

Option Strinct On という便利なコードはじめてしりました
暗黙の計算ってバグになるからできればやらないのが普通なのでしょうか?
実際にコピペして確かめてしました。
そしたら赤い線が引かれてわかりやすかったです。
ありがとうございます!mom