読者です 読者をやめる 読者になる 読者になる

堺風の頭部

徘徊、カメラ、PC、その他。

BASICのINT・FIXと、Excelと、負の数の四捨五入について

 高校くらいまで、よくBASICで遊んでいた。

 Windows時代になったら、どうもVisual BASICに適応できなくて長らく触らなくなって、もうプログラミングスキルといえるほどの能力もないけれど。VBAちょっとできる程度。

 

 それでふと、ExcelにもFIXED関数とINT関数があるのを見かけて、BASICにもINTとFIXがあったな、と思いだした。
 どれも小数を整数化する関数だけれど、少しずつ動作が違う。

 

 

 BASICのINT・FIX関数は、正の数だとどちらも同じ動作だった。
 四捨五入などはなく、単に整数部を切り捨てる。
 ではなぜ関数が違ったのかというと、負の数に対する動作が違った。
 INTだと、「入力した値を超えない最大の整数値を返す」ので、PRINT INT(-2.33)とすると、-3と表示される。
 FIXだと、単に小数部分を切り捨てるので、PRINT FIX(-2.66)とすると、-2と表示される。

 

 

 ではExcelを見てみると、まずINT関数はBASICと同様、「入力した値を超えない最大の整数値を返す」。
 =INT(-2.33)は-3となる。
 Excel 2016だと入力中にヘルプが出るけど、これでは「切り捨てて整数にした値を返す」という。負の数を与えたときに、この説明で正しいかどうかは疑問があるけれど。

 ExcelのFIXED関数は、任意桁まで四捨五入で丸め、文字列として返す(書式設定もできる)。BASICのFIX関数とは違う動作。
 負の数に対する四捨五入とはどうなるのか迷うが、=FIXED(-2.5,0)は-3が返った。5はゼロから遠い方に入れるようだ。

 ExcelはさすがにBASICより整数化関数が充実していて、まあ普通にはROUND関数を使うだろう。これは四捨五入だし、FIXEDと違って数値で返るので。
 切り捨て・切り上げなら、ROUNDUP, ROUNDDOWN関数がある。
 これもゼロから遠い方に対して切り上げるようで、=ROUNDUP(-2.33,0)は-3になる。

 

 

 私もBASIC触っていたのはだいぶ前だから、かなり記憶が怪しくなっていた。
 正直、「INTは四捨五入、FIXは入力値を超えない最大の整数」だとまで勘違いしていた。何一つあってない。
 だから、ここまでで「BASICで」と書いてあるのは、今頃になってぐぐって調べたので、Visual BASICでの話になる。

Int 関数、Fix 関数 (Visual Basic)

 まあ、BASICは昔からMicrosoftの製品だから、多分同じ動作をしてるだろう……とは思うんだけど、私が昔触ってたパソコンは日本のPC-98だから、ローカルルールで別の動作だった可能性もある。
 QuickBASICならPC-98用といえどMicrosoftの製品っぽさがあるけど、N88-BASIC(86)だとNECっぽさが強い。

 ただまあ、PC-98用でさえi8086 5MHzが最低スペック、8bit PC用の各社のBASICだともっとプアなCPUだから、あんまり凝った処理はしない気がする。
 切り捨てが基本動作、というのは自然じゃなかろうか。

 

 BASICで、小数点第1位を四捨五入した整数化、やるならどう書くだろう。

 BASICは、というかExcelでもできるんだけど、式の中に (n > 5)とかBooleanで値が返るような論理式を入れてやると、数字が返ってくる。
 Excelで =1+(1=1)なんて式をいれると、答えは2になる。=1+(1>1)だと、答えは1。TRUEが1、FALSEが0として計算される。
 QuickBASICあたりだと、TRUEが-1、FALSEが0だった。なんで符号が逆かは知らないが。

 これを活かして、小数点第一位が5以上かどうかを判断し、そうなら+1、そうでないなら+0する、という式が書けそうだ。

 小数点第一位だけ取り出すのは、まあ、10倍してから、10で割った余りを取ればよかろう。
 BASICでいえば、変数nに目的の値が入ってるとして、(n * 10) mod 10でいい。

 5以上かどうかの判断は、((n * 10) mod 10) => 5)。
 5以上ならこの式は-1を返し、5未満なら0を返す。

 なので、これをINTで整数化したnから引く。
 INT(n) - ((n * 10) mod 10) => 5)。

 これで、四捨五入での整数化ができた。はず。実機ないから試せないけど。
 この式の計算は、i8086とかV30とかでやったら、それなりに遅くなってたと思う。ループに入れて何千回とか繰り返したら、体感レベルで違ってたんじゃないかなあ。
 やっぱり30年前なら、INT関数は四捨五入なんて重い処理しないで切り捨て、というのがベターだったんじゃないかな。
 まあ、私がぱっと思いつく程度の計算式だから、もっと効率的な式があるとは思うんだけど。

 

 同じ式をExcelでやると、A1に元の数値が入ってるとして、=INT(A1)+(MOD(A1*10,10)>=5) でいい。

 ただ、負の数を与えたときの動作が、ROUND関数と異なる。
 ROUND関数は「5をゼロから遠い方に入れる」けれど、この式でやると「5を大きい方に入れる」ので、動作が異なる。

 どうも、ROUND関数の方が見た目には正しそうに感じるかもしれないが、数学的に間違ってる気がしなくもない。丸めてゼロになる数字の範囲だけ、他の数字より狭くなる。

 この辺どうなんだろう、と思ってぐぐったら、以下のようなサイトが見つかった。

digitalgatez.net

 JIS Z8401で決まってる、と来たぜ。

日本工業標準調査会:データベース検索-JIS検索

 ここで読んでみたら、たしかにそうなってた。つまりJIS的には、ExcelのROUND関数の動作が正しいようだ。