LoginSignup
0
1

More than 5 years have passed since last update.

c++ builder > 数値の精度にはまる > FLT_EPSILON > std::lround() / SimpleRoundTo()

Last updated at Posted at 2016-03-23

自作のグラフソフトでバグが見つかった。

以下の計算において、majorTickIncrement = 0.5, minorTickIncrement = 0.1の時に以下の計算が結果minorTickCount が3になる(本来は4)。

int minorTickCount = axisPtr->majorTickIncrement / axisPtr->minorTickIncrement - 1;

詳細を見ていくと minorTickIncrement (フォーム入力)の値は0.1と入力した値が 0.100000001490116 になっている。
この誤差と割り算の仕組みにより 3 になるようだ。

(ちなみに 5.000... / 1.0.... - 1)の計算は4になり上記と様相が異なる。

対応1 > FLT_EPSILON

FLT_EPSILON分の誤差がminorTickIncrement に含まれたのかと思い以下のようにしたらきちんと4になった。

ただし、このあたりは詳細に見ていかないといけないだろう。

#include <float.h>
...
float fval1 = axisPtr->majorTickIncrement;
float fval2 = axisPtr->minorTickIncrement;
fval2 = fval2 - FLT_EPSILON;
int minorTickCount = fval1 / fval2 - 1;

上記のコードでFLT_EPSILONを引かさない計算では3になる。
floatは7桁の精度だったと思うが、なぜ3になるかは未消化。

ideoneで確認

http://ideone.com/ftpjlB
で確認したところ 4 になる。

C++ Builderのコンパイラの問題なのだろうか。

対応2 > std::lround() / SimpleRoundTo()

@termoshtt さんに std::lround()があるという情報をいただいた。

XE4でinclude <cmath>を追加してstd::lround()を使おうとしたが、エラーのため使用できなかった。

lround()をキーとして検索するとXE4では SimpleRoundTo()という関数が近い処理をしているようだ。
以下はきちんと4になった。

float fval1 = axisPtr->majorTickIncrement;
float fval2 = axisPtr->minorTickIncrement;
int cnt3 = SimpleRoundTo(fval1 / fval2 - 1.0);
int nop=1; // for breakpoint

こちらもでいいだろう。

int cnt3 = SimpleRoundTo(fval1 / fval2) - 1;
0
1
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1