• 締切済み

Excel Vbaの関数Intの機能について

Excel Vbaでの関数Intの機能について教えてください ある特定の値についての処理が私の予想に反した動きをします。私の理解が浅いと思うのですがその理由が分かりません。 Vbaでプロシジャーを作ってみました。ともに小数0.3を10倍し、 その値とIntの値を比較しています。コードの違いは(1)と(2)です。 Public Sub Test1() Dim dblNum1 As Double, dblNum2 As Double dblNum1 = 0.2 dblNum1 = dblNum1 + 0.1 ……(1) dblNum2 = dblNum1 * 10 Debug.Print "Int(dblNum2) = " & Int(dblNum2); " : dblNum2 = " & dblNum2 Debug.Print "差(Test1):" & dblNum2 - Int(dblNum2) End Sub Public Sub Test2() Dim dblNum1 As Double, dblNum2 As Double dblNum1 = 0.3 ' dblNum1 = dblNum1 + 0.1 ……(2)コメントにしています dblNum2 = dblNum1 * 10 Debug.Print "Int(dblNum2) = " & Int(dblNum2); " : dblNum2 = " & dblNum2 Debug.Print "差(Test2):" & dblNum2 - Int(dblNum2) End Sub 実行結果は次のようになりました。 Test1の結果 Int(dblNum2) = 3 : dblNum2 = 3 差(Test1):4.44089209850063E-16 Test2の結果 Int(dblNum2) = 3 : dblNum2 = 3 差(Test2):0 差(Test1)がなぜ0にならないのか不思議な気がします(当たり前なのかな?)。 0.8についても同様な結果になります。 その他の値0.1 ~ 0.9では、私の予想通り、差(Test1)、差(Test2)ともに0になります。 ネットなどで検索したら、浮動小数点の扱いに関係がありそうだなのかなぐらいは想像できるのですが具体的な仕組みはよく分かりません。 この疑問に至った背景について、説明します。 Int関数の機能については特段興味はなかったのですが、「有限小数の小数点以下の桁数を調べる関数」を作るのに利用していました。その関数では、なぜか特定の小数についてはうまく機能しませんでした。調べていくうちに上記のような現象に気づきました。 話がそれて厚かましいお願いになりますが、「有限小数の小数点以下の桁数を調べる関数」のコーディングの例を教えていただけないでしょうか。私が作った関数は以下のようになります。これについてもアドバイスをいただければ大変助かります。   Public Function FiniteDCPlace(dblR As Double, intMax As Integer) As Integer '機能:小数点以下の何桁の有限小数になるか、その桁数を調べる。 '引数:dblRは小数、intMax は調べる最大の桁数 ただしintMaxは7以下の値 '戻り値:小数点以下の桁数がintMax以下の有限小数の場合、その桁数 ' それ以外は「-1」 Dim i As Integer Dim dblNum As Double If intMax > 7 Then FiniteDCPlace = -1 Exit Function Else 'No Ope End If dblNum = dblR FiniteDCPlace = -1 For i = 0 To intMax Debug.Print "Int(dblNum) = " & Int(dblNum) & " : dblNum = " & dblNum Debug.Print "差(FiniteDCPlace):" & dblNum - Int(dblNum) If Int(dblNum) = dblNum Then FiniteDCPlace = i Exit For Else dblNum = dblNum * 10 FiniteDCPlace = -1 End If Next End Function 次のプロシジャーでチェックしました。 小数 0.028 のとき3ではなく、-1を返します。 Private Sub testFiniteDCPlace() Dim intNum As Integer Dim dblNum As Double Dim i As Integer dblNum = 0.028 ‘予想外の結果になる intNum = FiniteDCPlace(dblNum, 7) Debug.Print dblNum & " の桁数 : " & intNum dblNum = 0.027 ‘予想通りの結果になる 3になる intNum = FiniteDCPlace(dblNum, 7) Debug.Print dblNum & " の桁数 : " & intNum End Sub ちなみに、環境はOS Widows10 、Excel2013 です。 以上よろしくお願いします。 質問の内容を書いてください

みんなの回答

回答No.4

回答No.2-3 です。何度もすみません。 前スレで、> ちなみに小数は8桁以内の整数と8桁以内の整数の商(割り算)になります。 とご説明があったのを念頭に(というより思い込みで)応えていましたが、 一応、もっと桁数が多い場合の汎用性を考えると、 オーバーフローを回避する意味では、(No.2のCLng()よりも)そもそもの、 Int()関数を使った方が簡単で良かったのかも知れません。 なので、Int()関数を使った直接的な回答を。 ' ' /// 十進(固定)小数点数に変換、整数に丸めた数値との文字長の差を求める /w9076681 Public Function FiniteDCPlaceA(ByVal numDec As Variant, Optional ByVal lngMax As Long = 7&) As Long   If lngMax > 7& Then     FiniteDCPlaceA = -1&   Else     numDec = CDec(numDec)     FiniteDCPlaceA = Len(numDec - Int(numDec)) - 2     If FiniteDCPlaceA < 0 Then FiniteDCPlaceA = 0     If FiniteDCPlaceA > lngMax Then FiniteDCPlaceA = -1 ' ? それ以外は「-1」 ?   End If End Function ' ' /// Private Sub testFiniteDCPlaceA() Dim lngNum As Long Dim dblNum As Double   dblNum = 0.28   lngNum = FiniteDCPlaceA(dblNum)   Debug.Print dblNum & " の小数点以下の桁数 : " & lngNum   dblNum = 0.27   lngNum = FiniteDCPlaceA(dblNum)   Debug.Print dblNum & " の小数点以下の桁数 : " & lngNum End Sub ' ' /// ついでに、回答No.2で触れた"小数点の位置"版もあげておきます。 この場合は整数に変換する(丸める)必要がありません。 ' ' /// 十進(固定)小数点数に変換後、小数点数の文字長と小数点の位置との差を求める /w9076681 Public Function FiniteDCPlaceJ(ByVal numDec As Variant, Optional ByVal lngMax As Long = 7&) As Long Dim lngDecPointPos As Long   If lngMax > 7& Then     FiniteDCPlaceJ = -1&   Else     numDec = CDec(numDec)     lngDecPointPos = InStr(numDec, ".")     If lngDecPointPos Then FiniteDCPlaceJ = Len(numDec) - lngDecPointPos     If FiniteDCPlaceJ > lngMax Then FiniteDCPlaceJ = -1 ' ? それ以外は「-1」 ?   End If End Function ' ' /// Private Sub testFiniteDCPlaceJ() Dim lngNum As Long Dim dblNum As Double   dblNum = 0.28   lngNum = FiniteDCPlaceJ(dblNum)   Debug.Print dblNum & " の小数点以下の桁数 : " & lngNum   dblNum = 0.27   lngNum = FiniteDCPlaceJ(dblNum)   Debug.Print dblNum & " の小数点以下の桁数 : " & lngNum End Sub ' ' /// 他にも方法はたくさんあると思いますが、 私の場合は何れにしてもCDec()関数(場合によってはCCur()関数)を使います。 それから、前回のご質問の時から気になっていたのですが、 セルに表示されている(数値ではなくて)数字(文字列)を取得する range.Text プロパティの扱い方を確認しておくと、 もしかしたら何かの役に立つこともあるのかも知れません。   Cells(1, 1).Text   Range("A1").Text などのように、単一セルに対して用います。 、、、見当外れでしたら、この件は無視してください。 意図にそぐわないとか、不足や疑問が残るようでしたら、補足欄にでも書いてみて下さい。以上です。

EulerKnowsNo
質問者

お礼

realbeatin 様 お礼が遅くなりました。 質問内容がタイトルから外れたものになったにもかからわらず、丁寧に回答をいただきありがとうございます。小数点以下の桁数を求める関数3つともうまくいきました。「小数点の位置」版が一番分かりやすいように思いました。自分もMSDNの「データ型のトラブルシューティング(Visual Basic)-浮動小数点式での比較が等しくならない」を参考に修正版を作り、うまくいきました。 今回の回答で勉強させてもらったこと。 >二進数【浮動小数点数】を十進数【小数点数】Variant/Decimalに変換してから・・・ そもそも、Decimal(10進数型)を知りませんでした。(はるか昔に勉強した?) 型変換の関数についてもほとんど知識がありませんでした。 どの場面でどのように使えばいいのかの経験を積んでいきたいと思います。 >それから、前回のご質問の時から気になっていたのですが、 >セルに表示されている(数値ではなくて)数字(文字列)を取得する >range.Text プロパティの扱い方を確認しておくと、 >もしかしたら何かの役に立つこともあるのかも知れません。 セルに表示されている(数値)を計算処理しないのであれば(文字列)として扱うという発想が全くありませんでした。桁数を求める関数でも同様でした。数値の場合はrange.Value プロパティしか使っていませんでした。 今回の質問のきっかけは、生徒が演習した結果を自動採点する教材を作る中での疑問でした。「表示形式->小数点以下の3位の設定」の判定で正解と生徒答案のNmberFormatLocalを比較していました。計算結果がたまたま有限小数3位のセルがあり、生徒が書式設定をしなくて「標準/G」のままで、チェックがうまくいきませんでした(チェックから「標準/G」を外していた)。授業の課題が「書式設定」の場合は、従来通りNumberFormatLocalを利用する。 印刷結果を重視(見た目がすべて)する場合はrange.Textを利用することにしました。 浮動小数点の精緻な数値計算については特に興味はなっかたのですが、疑問をすぐに解消したくて、本来の目的からそれた質問をしてしまいました。 realbeatin 様 3度もお世話になりました。 3回の質問とも「そのことが分かってそれが、なんやねん。そもそもお前は何がしたいねん。」と突っ込まれそうな内容にも関わらず丁寧に対応していただき本当にありがとうございました。

回答No.3

解答No.2、補足修正です。 /// > 浮動小数点数を相互に(四則)演算させる場合には、 > 基本的に演算誤差が出ます。 > なのでVBAでは、二進数を十進数Variant/Decimalに変換してから演算します。 > > それでも、厳密には誤差が発生する場合もあるにはあるのですが、 > 7桁という縛りがあるるようですから問題なく扱える、と言って良いでしょう。 ↓ 【二進数である】浮動小数点数を相互に(四則)演算させる場合には、 基本的に演算誤差【(丸め誤差)】が出ます。 なので【今回必要とされる関数については】VBAでは、 二進数【浮動小数点数】を 十進数【小数点数】Variant/Decimalに変換してから 演算【させる必要があります】。 それでも、厳密には【丸め】誤差が発生する場合もあるにはあるのですが、 【今回必要とされる関数については】 7桁という縛りがあるるようですから問題なく扱える、と言って良いでしょう。 /// 以上、誤解を招き易い表記がありましたので、読み替えてあげてください。

回答No.2

こんにちは。 浮動小数点数は、Double型だけではありません。   Single、Double、Currency、Date、Variant/Decimal なので、引数として受ける場合はVariant型が必須です。 ただ、そもそもがExcelのセルの値だけを判別する目的なのでしたら、 それは、Double、Currency、Dateの3種類だけ相手にすればいいことになりますが、 Currency、DateをDouble型の引数で受ければ、それだけでも誤差が生じることになります。 Excelのセルの値だけを判別する目的で考えたら、 Excelのシート上で、Excel関数を使った数式で計算させた方が一層簡単でしょうから、 場合によってはVBAからシート上で計算させる方法もある、 ということを念頭に入れておいてください。 #ただし、Excelのシート上でも、浮動小数点数相互の(四則)演算については、  精度を期待できるものではありません。桁数を求めるならば、という話です。 浮動小数点数を相互に(四則)演算させる場合には、 基本的に演算誤差が出ます。 なのでVBAでは、二進数を十進数Variant/Decimalに変換してから演算します。 それでも、厳密には誤差が発生する場合もあるにはあるのですが、 7桁という縛りがあるるようですから問題なく扱える、と言って良いでしょう。 そちらで整数部分の桁数を求める為にInt()関数を使っている部分を、 Clng()関数とAbs()関数の組合わせにしています。 十進整数と十進小数点数との文字長の差を求めます。 どうせ文字列を扱うのなら、十進小数点数だけを相手にして、 文字長と小数点の位置を比較した方が効率的(整数を扱う必要はない)ですが、 この点については、敢えてご提示のものを踏襲します。 ※プロシージャ名、替えています。 ' ' /// Public Function FiniteDCPlace0(ByVal numDec As Variant, Optional ByVal lngMax As Long = 7&) As Long   If lngMax > 7& Then     FiniteDCPlace0 = -1&   Else     numDec = CDec(numDec)     FiniteDCPlace0 = Len(Abs(numDec - CLng(numDec))) - 2     If FiniteDCPlace0 < 0 Then FiniteDCPlace0 = 0     If FiniteDCPlace0 > lngMax Then FiniteDCPlace0 = -1 ' ? それ以外は「-1」 ?   End If End Function ' ' /// Private Sub testFiniteDCPlace0() Dim lngNum As Long Dim dblNum As Double   dblNum = 0.28   lngNum = FiniteDCPlace0(dblNum)   Debug.Print dblNum & " の小数点以下の桁数 : " & lngNum   dblNum = 0.27   lngNum = FiniteDCPlace0(dblNum)   Debug.Print dblNum & " の小数点以下の桁数 : " & lngNum End Sub

  • dogs_cats
  • ベストアンサー率38% (278/717)
回答No.1

専門家では無く、バージョンも2007ですので、ご参考まで。 浮動小数点で結果が得られない時は、変数を文字列変換後数値に戻すと正常値を得られる事があります。 Public Sub Test1() Dim dblNum1 As Double, dblNum2 As Double dblNum1 = 0.2 dblNum1 = Val(CStr(dblNum1 + 0.1))で解消しました。 dblNum = dblRは dblNum = Val(CStr(dblR))やdblNum = Val(CStr(dblNum))でも解消しませんでした。 intとは関係ない10倍した箇所を修正した所、正常値となりました。 dblNum = Val(CStr(dblNum)) * 10 dblNum = 0.028でしか試していませんので、他のdblNumもVal(CStr(dblNum)) に変更すべきかは他の数値での結果で判断下さい。

EulerKnowsNo
質問者

お礼

Dogs_cats さん 回答ありがとうございます。 >浮動小数点で結果が得られない時は、変数を文字列変換後数値に戻す・・・ 参考になりました。アドバイスいただいた修正は自分でも確認できました。 型変換の関数をほとんど使ったことがなかったので、Vba のヘルプ・MSDNのホームページなどであらためて調べてみました。いろいろな関数があることが分かり、勉強になりました。関数Val,CStr についても機会があれば使っていき理解を深めていきたいと思います。 お礼が遅くなりましたが、ありがとうございました。

関連するQ&A

  • 変数を続けて宣言した場合はEmpty型になる?

    Sub test1() Dim Int1, Int2 As Long Debug.Print "---------- test1の実行結果 ----------" Debug.Print TypeName(Int1) Debug.Print TypeName(Int2) End Sub Sub test2() Dim Int1 As Long Dim Int2 As Long Debug.Print "---------- test2の実行結果 ----------" Debug.Print TypeName(Int1) Debug.Print TypeName(Int2) End Sub を実行すると、 ---------- test1の実行結果 ---------- Empty Long ---------- test2の実行結果 ---------- Long Long の結果が得られます。 test1のInt1がEmpty型になりますが、変数の型にEmptyはないですよね? どういうことなのでしょう???

  • 乱数の発生 Randomizeを入れた方が良いのか

    VBAについて教えてください。 --------------------------- Sub Randomizeを入れた場合() Dim intMax As Integer Dim intMin As Integer Randomize intMin = 1 intMax = 10 Debug.Print Int((intMax - intMin + 1) * Rnd + intMin) End Sub --------------------------- Sub Randomizeを入れていない場合() Dim intMax As Integer Dim intMin As Integer intMin = 1 intMax = 10 Debug.Print Int((intMax - intMin + 1) * Rnd + intMin) End Sub --------------------------- 上記二つのマクロを実行しても 同じような結果が得られる気がするのですがRandomizeは必要なのでしょうか? 一つのサブプロシージャーの中で1回しか乱数を発生させないのなら Randomizeを使って乱数を初期化する必要はないのですか?

  • 変数iもjも同じく値を保持できるからどちらを使って

    このサンプルコードは、 変数iもjも同じく値を保持できるから、test1を使ってもtest2を使っても一緒なのでしょうか? Option Explicit Dim i As Integer Sub test1() Static j As Integer j = j + 1 Debug.Print j End Sub Sub test2() i = i + 1 Debug.Print i End Sub ご回答よろしくお願いします。

  • VBA AからZで乱数を発生させたい

    Sub AからZで乱数を発生させる() Dim 最大値 As Integer Dim 最小値 As Integer Randomize 最小値 = 1 最大値 = 10 Debug.Print Int((最大値 - 最小値 + 1) * Rnd + 最小値) End Sub --------------------------------------------------------- これを実行すると、1から10の間で乱数を発生させられるのですが、 AからZの間で乱数を発生させたい場合は、 このコードをどのようにすればいいでしょうか? アドバイスよろしくお願いします。

  • VBA 複数の文字のコードを一気に返すには

    Sub test1() Dim myStr As String myStr = "abc" Debug.Print Asc("a") Debug.Print Asc(myStr) End Sub このコードは、どちらも97が返るのですが、 myStrは3文字です。 3文字全ての文字コードを返すには、 Sub test2() Dim myStr As String myStr = "abc" Debug.Print Asc(Mid(myStr, 1, 1)) & Asc(Mid(myStr, 2, 1)) & Asc(Mid(myStr, 3, 1)) End Sub のようにするしかないのでしょうか?

  • Access97 Int関数

    小数点の切り捨てをしたくてクエリーで 以下の計算をしてみました。 =Int([個数]*[単価]) ところが以下の数値で計算させると結果が 50232となると思うのですが50231となってしまいます。 数量:460 単価:109.2 結果:50231(電卓で計算すると50232) 結果が小数点になるときは問題ないようです。 使い方が間違っているのでしょうか。 こういう場合Int関数を使うのが誤りなのでしょうか? だとしたら他に当てはまる関数があるのでしょうか? ちなみにテーブルの定義としては 数量:長整数型(小数点以下桁数0) 単価:単精度浮動小数点型(小数点以下桁数0) となっていますが小数点は表示されています。

  • 【Excel:VBA】千円未満切捨ての方法

    1000未満切捨てのVBA関数はどの関数を使いどのように引数を指定すればよいのかわかりません。 ------------------------------- Sub 切捨て() Dim x As Integer x = 123456 〔切捨て関数〕 MsgBox x End Sub -------------------------------- この状態で、メッセージボックスに「123000」の値が表示されるようにできないでしょうか? 小数点以下を切り捨てるには x = Int(x) だとわかったのですが、この関数に千未満切捨てなどを指定する引数はあるのでしょうか?

  • awk の int()に関数について

    数値処理でawkを使い始めているのですが, int関数を使って小数を切り捨てたいのですが, 以下のような現象が起こって困っています。 print int(9.53*100) 952 ? print int(9.53*1000) 9530 OK print int(65.52*1000) 65519 ? print int(65.52*1000.0+0.1) 65520 ? int関数はどういう動作をするのかどなたかご教示いただけますでしょうか。 お願いいたします。

  • lenは文字数を取得する関数ですよね?

    vbaで Sub test() Dim i As Long Debug.Print Len(i) End Sub とすると、4が返るのですが、なぜなのかまったくわかりません。 iには0が入っているので、一文字だから1が返ると思うのですが・・・

  • VBA "double"から0.1を引くと・・・

    エクセルVBAですが、double型で変数を定義し、-80を代入します。 そこから0.1ずつを引いていきます。すると、途中から桁がおかしくなってしまいます。 (注:別に-80からでなくても、どの値からでも、また、引いていっても足していっても、遅かれ早かれ どこかから桁がおかしくなってしまいます。) -80 -80.1 -80.2 …中略 -80.8 -80.8999999999999 -80.9999999999999 … といった感じです。 どうしてこのようになってしまうのでしょうか。 また、このようにならないためにはどうしたらよいのでしょうか。 いかにサンプルプログラムを載せます。 ------------------------------------------ Sub test() Dim test As Double Dim i As Integer test = -80 For i = 1 To 100 test = test - 0.1 Cells(i, 1) = test Next i End Sub ----------------------------------

専門家に質問してみよう