Excel VBA 質問スレッド No.147 (解決済)

配列とIf文の組み合わせ

投稿者 : たくや     投稿日時 : 2020/12/21(Mon) 23:09:37     OS : Windows 10     EXCEL : 未指定
初めまして。

AK4セルに0.02
AL4セルに0.2
という数字があらかじめ入力されています。

A列からF列には
数値データが100行くらい入力されています。

以下、★マークのところがうまくいかないので
ご教授いただけませんでしょうか。

長文で恐縮ですが、よろしくお願いいたします。

Sub SAR()

Dim lastrow As Integer
Dim i As Integer
Dim length(2) As Integer

lastrow = (Range("A5").End(xlDown).Row)

length(1) = ActiveSheet.Range("AK4").Value
length(2) = ActiveSheet.Range("AL4").Value

For i = lastrow To 6 Step -1

If i = lastrow Then '1回目の処理

Cells(i, 35) = Cells(i, 3)
Cells(i, 36) = Cells(i, 4)
Cells(i, 37) = 0
Cells(i, 38) = 0
Cells(i, 39) = 0
Cells(i, 40) = 0
Cells(i, 41) = 0

Else '2回目以降の処理

If Cells(i + 1, 37) = 0 And Cells(i + 1, 38) = 0 Then

If (Cells(i, 3) - Cells(i + 1, 3) = Cells(i + 1, 4) - Cells(i, 4)) Or (Cells(i, 3) - Cells(i + 1, 3) <= 0 And Cells(i + 1, 4) - Cells(i, 4) <= 0) Then

Cells(i, 35) = (略)
Cells(i, 36) = (略)
Cells(i, 37) = 0
Cells(i, 38) = 0
Cells(i, 39) = 0
Cells(i, 40) = 0
Cells(i, 41) = 0

ElseIf Cells(i, 3) - Cells(i + 1, 3) > Cells(i + 1, 4) - Cells(i, 4) Then

Cells(i, 37) = (略)
Cells(i, 38) = (略)
Cells(i, 39) = (略)
Cells(i, 40) = length(1) '★「length(1)」を「0.02」にするとうまくいきます
Cells(i, 41) = (略)

ElseIf Cells(i, 3) - Cells(i + 1, 3) < Cells(i + 1, 4) - Cells(i, 4) Then

Cells(i, 37) = (略)
Cells(i, 38) = (略)
Cells(i, 39) = (略)
Cells(i, 40) = length(1) '★「length(1)」を「0.02」にするとうまくいきます
Cells(i, 41) = (略)

Else

Cells(i, 37) = "Error"
Cells(i, 38) = "Error"
Cells(i, 39) = "Error"
Cells(i, 40) = "Error"
Cells(i, 41) = "Error"

End If

Else

If Cells(i + 1, 37) < Cells(i + 1, 38) Then

If Cells(i, 4) < Cells(i + 1, 37) + Cells(i + 1, 41) Then

Cells(i, 37) = (略)
Cells(i, 38) = (略)
Cells(i, 39) = (略)
Cells(i, 40) = length(1) '★「length(1)」を「0.02」にするとうまくいきます
Cells(i, 41) = (略)

ElseIf Cells(i, 4) > Cells(i + 1, 37) + Cells(i + 1, 41) Then

If Cells(i + 1, 37) + Cells(i + 1, 41) > WorksheetFunction.Min(Range("D" & i + 1), Range("D" & i + 2)) Then

If Cells(i + 1, 40).Value = length(2) Then '★「length(2)」を「"0.2"」にするとうまくいきます

Cells(i, 37) = (略)
Cells(i, 38) = (略)
Cells(i, 39) = (略)
Cells(i, 40) = (略)
Cells(i, 41) = (略)

Else

If Cells(i, 3) > Cells(i + 1, 38) Then

Cells(i, 37) = (略)
Cells(i, 38) = (略)
Cells(i, 39) = (略)
Cells(i, 40) = Cells(i + 1, 40) + length(1) '★「length(1)」を「0.02」にするとうまくいきます
Cells(i, 41) = (略)

ElseIf Cells(i, 3) <= Cells(i + 1, 38) Then

Cells(i, 37) = (略)
Cells(i, 38) = (略)
Cells(i, 39) = (略)
Cells(i, 40) = (略)
Cells(i, 41) = (略)

Else

Cells(i, 37) = "Error"
Cells(i, 38) = "Error"
Cells(i, 39) = "Error"
Cells(i, 40) = "Error"
Cells(i, 41) = "Error"

End If

End If

~中略~

Else

Cells(i, 37) = "Error"
Cells(i, 38) = "Error"
Cells(i, 39) = "Error"
Cells(i, 40) = "Error"
Cells(i, 41) = "Error"

End If

Else

Cells(i, 37) = "Error"
Cells(i, 38) = "Error"
Cells(i, 39) = "Error"
Cells(i, 40) = "Error"
Cells(i, 41) = "Error"

End If

Else

Cells(i, 37) = "Error"
Cells(i, 38) = "Error"
Cells(i, 39) = "Error"
Cells(i, 40) = "Error"
Cells(i, 41) = "Error"

End If

End If

End If

Next

Range("AI6", "AO" & lastrow).NumberFormatLocal = "0.00"

Columns("AI:AJ").Hidden = True

End Sub

[返信 1] Re : 配列とIf文の組み合わせ
投稿者 : EUNO     投稿日時 : 2020/12/22(Tue) 12:53:59
コードが長くて読んでないのですが、たぶんIntegerが原因でしょう。
IntegerやLongは整数型です。小数点型はSingleになります。

[返信 2] Re : 配列とIf文の組み合わせ
投稿者 : たくや     投稿日時 : 2020/12/23(Wed) 07:33:01
EUNOさん、ありがとうございます!!

おかげで1つ目の問題が解決しましたが、
2つ目の問題がまだ解決しません。

AL4セルに0.2という数字が
あらかじめ入力されています。

--------
Dim i As Integer
Dim af_max As Single

af_max = ActiveSheet.Range("AL4").Value
--------

としました。

そしてFor文の中で

--------
If Cells(i + 1, 40).Value = af_max Then
--------

としたんですが、
これがうまくいきません。

「もしCells(i + 1, 40)の値が0.2ならば」

というふうにしたいです。

何度もお手数をおかけして恐縮ですが、
よろしくお願いいたします。

[返信 3] Re : 配列とIf文の組み合わせ
投稿者 : EUNO     投稿日時 : 2020/12/23(Wed) 11:25:54
それで合っているとは思いますが、どんなエラーがでるか分かりますか?

コードをシンプルにしてデバッグしてみてください。
たとえばこんな感じで。

Sub test()

Dim i As Integer
Dim af_max As Single

af_max = ActiveSheet.Range("AL4").Value

For i = 1 To 10
If Cells(i + 1, 1).Value = af_max Then
 Cells(i + 1, 2).Value = "OK"
End If

Next i

End Sub

[返信 4] Re : 配列とIf文の組み合わせ
投稿者 : たくや     投稿日時 : 2020/12/24(Thu) 22:10:28
遅くなってすみません。

Singleを使うとなぜか0.02が
「0.0199999995529652」
という細かい数字になってしまったので、
Doubleを使わせていただきました。

A列の1行目から20行目まで
1~20の数字を上から順番に入れています。
(数字に意味はありません)

■ケース①
Sub test()

Dim lastrow As Integer
Dim i As Integer
Dim af As Double
Dim af_max As Double

lastrow = (Range("A1").End(xlDown).Row)

af = 0.02
af_max = 0.2

For i = lastrow To 1 Step -1

If i = lastrow Then '1回目の処理

Cells(i, 2) = af

Else '2回目以降の処理

If Cells(i + 1, 2).Value = af_max Then

Cells(i, 2) = af_max

Else

Cells(i, 2) = Cells(i + 1, 2) + af

End If

End If

Next i

End Sub

↑こうするとうまくいきませんが、

■ケース②
Sub test()

Dim lastrow As Integer
Dim i As Integer
Dim af As Double

lastrow = (Range("A1").End(xlDown).Row)

af = 0.02

For i = lastrow To 1 Step -1

If i = lastrow Then '1回目の処理

Cells(i, 2) = af

Else '2回目以降の処理

If Cells(i + 1, 2).Value = "0.2" Then

Cells(i, 2) = "0.2"

Else

Cells(i, 2) = Cells(i + 1, 2) + af

End If

End If

Next i

End Sub


↑こうするとうまくいきます。

「うまくいく」というのは、
私の望み通りの処理を行なってくれるということです。

「望み通りの処理」というのは、
下(20行目)から上(1行目)に向かって
順番に行なっていく処理なのですが、

B列の初期値が0.02、

1行上に進むごとに
B列の数字が0.02ずつ加算されていきます。

ですがMAXは0.2としたいです。

これが
ケース①だとうまくいきませんが、
ケース②だとうまくいきます。

ケース①でもうまくいくようにしたいです。
何が問題でしょうか。

[返信 5] Re : 配列とIf文の組み合わせ
投稿者 : ヘンリー     投稿日時 : 2020/12/25(Fri) 09:13:34
>Singleを使うとなぜか0.02が
>「0.0199999995529652」

という事は、実際のセルの値は0.02でない可能性があります。
まずは、セルの表示形式で、数値型にして、
小数点の桁数を増やして確認してみてください。

これで0.02000000…と表示されるようなら、
SingleでもDoubleでも同じ結果になるはずです。

コンピュータは小数点以下の計算は苦手です。
そのため、多くは浮動小数点という方式で、小数点以下を表します。

浮動小数点について、以下のサイトがわかりやすいかと思います。
https://wa3.i-3-i.info/word14960.html

確実なのは、Cells(i, 2)や、Cells(i + 1, 2)に対して、
WorksheetFunctionのRoundやRoundUpやRoundDownを使って
小数点第〇位までのデータに直すことです。

小数点以下が第4位までであれば、
固定小数点という方法を使う方法もあります。
ただし、この場合も0.0199529652というようなデータが入っていれば、
意図した結果が出ないこともあると思われます。

演算誤差について、以下のサイトがわかりやすいかと思います。
http://officeboole.com/study_aid04.html


いずれにしろ、プログラミングをするのであれば、
『変数と変数の型』について、今一度勉強しなおすことをお勧めします。
そして、変数でなくても、全てにおいて『型』を意識することが大切です。

理由は、Cells(i, 2) = "0.2"と書いているからです。
""で囲まれているものは、VBAでは文字列を表します。

本来、数値をセルに代入するのであれば、
Cells(i, 2) = 0.2
と書くべきです。
※ダブルコーテーションで囲まない

例えば、文字列の"0.2"に文字列の"0.2"を足すと、"0.20.2"となります。
例)Cells(1, 1) = "0.2" + "0.2"と、
  Cells(1, 2) = "0.2" + 0.2を
  新規ファイルで実行してみてください。

VBAでは『暗黙の型変換』により、"0.2"と書いても
Double型に変換される場合があったり、
Excel上では、Cells(i, 2)の表示形式によって、
数値型に変換されることがあります。

この様に『型』に意識がないと、またいずれどこかで
意図しない結果になる事が考えられますので、
是非、『変数と変数の型』、『型』について
常に意識する事を心がけてみて下さい。

[返信 6] Re : 配列とIf文の組み合わせ
投稿者 : たくや     投稿日時 : 2020/12/25(Fri) 12:34:16
ヘンリーさん
ご返信ありがとうございます。

まずSingleとDoubleの違いについて
質問させてください。

■Singleの場合
---------
Dim af As Single
af = 0.02
Range("A1") = af
---------
↑このようにすると、A1セルに入力されるのは
「0.0199999995529652」です。

■Doubleの場合
---------
Dim af As Double
af = 0.02
Range("A1") = af
---------
↑このようにすると、A1セルに入力されるのは
「0.02」です。

なぜこうなってしまうのでしょうか?

セルの表示形式は関係なく、
数式バーに表示されているのが上記の数字なんです。

ちなみに私が使用しているExcelは
Microsoft365の32ビットバージョンです。

32ビットにしている理由は
ActiveXコントロールが
64ビットではうまく動作しなかったからです。

パソコンはWindows10です。

[返信 7] Re : 配列とIf文の組み合わせ
投稿者 : EUNO     投稿日時 : 2020/12/25(Fri) 14:32:45
少数の演算誤差の問題のようですね。

singleよりdoubleの方が精度が高い
変数に代入するより数値を直接入れる方が精度が高い  

のようです。どんな処理をしたいかで対処方法は変わってくると思います。

[返信 8] Re : 配列とIf文の組み合わせ
投稿者 : たくや     投稿日時 : 2020/12/25(Fri) 17:25:35
次のようにしたら
問題が解決しました!!!
Sub test()

Dim lastrow As Integer
Dim i As Integer
Dim af As Double
Dim af_max As String '★ここがポイント!!

lastrow = (Range("A1").End(xlDown).Row)

af = 0.02
af_max = 0.2

For i = lastrow To 1 Step -1

If i = lastrow Then '1回目の処理

Cells(i, 2) = af

Else '2回目以降の処理

If Cells(i + 1, 2) = af_max Then

Cells(i, 2) = af_max

Else

Cells(i, 2) = Cells(i + 1, 2) + af

End If

End If

Next i

End Sub

Dim af As Single
Dim af_max As Single
↑こうするとうまくいきません。

Dim af As Double
Dim af_max As Double
↑こうしてもうまくいきません。

Dim af As Single
Dim af_max As Double
↑こうしてもうまくいきません。

Dim af As Double
Dim af_max As Single
↑こうすると表面的にはうまくいきますが、
10行目より上に入力される数値が0.200000002980232になります。

当掲示板について
返信入力フォーム
お 名 前  :
内  容   :
ステータス  : この質問を解決済みにする

認証コード  :
        キャプチャ画像


( 処理日時 : 2021-01-27 12:01:51 )

Page
Top