GoogleTestで浮動小数点数を比較する

# 目的
GoogleTestを使って、浮動小数点数を比較する際の要点を紹介します。
本記事ではFloat型（単精度）について説明しますが、Double型（倍精度）でも考え方は同じです。
#Floatについて

Floatは32bitの内仮数部が24bitしかないため、有効数字の上限は約７桁となります。
[参考文献](https://qiita.com/tobira-code/items/d9c40b650dec40d3c2ed)
#テストマクロの種類
GoogleTestに用意されている、値一致比較のマクロは以下の３種類

- **EXPECT_EQ(ASSERT_EQ)**
- **EXPECT_FLOAT_EQ(ASSERT_FLOAT_EQ)**
- **EXPECT_NEAR(ASSERT_NEAR)**

# EXPECT_EQは使えない

[公式ドキュメント](http://opencv.jp/googletestdocs/advancedguide.html#adv-floating-point-comparision)にも記載がある通り、
**EXPECT_EQ**は完全一致比較を行うため、浮動小数点数の比較には使用できません。
よって、下記のテストは失敗します。

```cpp:test_float.cpp
TEST(FloatTest, FailureTestForSimpleCalculation){

  float a, b;

  a =  555555.5;
  b =  0.055555;

  EXPECT_EQ(555555.555555, a+b); //失敗する
}
```
```terminal:テストログ
----------------------------------------------------------
1/4 Testing: FloatTest.FailureTestForSimpleCalculation
1/4 Test: FloatTest.FailureTestForSimpleCalculation
Command: ".../AllTests" "--gtest_filter=FloatTest.FailureTestForSimpleCalculation"
Directory: .../build/test
"FloatTest.FailureTestForSimpleCalculation" start time: Aug 05 19:11 JST
Output:
----------------------------------------------------------
Running main() from /.../googletest/src/gtest_main.cc
Note: Google Test filter = FloatTest.FailureTestForSimpleCalculation
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from FloatTest
[ RUN      ] FloatTest.FailureTestForSimpleCalculation
/.../test/test_float.cpp:10: Failure
Expected equality of these values:
  555555.555555
    Which is: 555556
  a+b
    Which is: 555556
[  FAILED  ] FloatTest.FailureTestForSimpleCalculation (0 ms)
[----------] 1 test from FloatTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] FloatTest.FailureTestForSimpleCalculation

 1 FAILED TEST
<end of output>
Test time =   0.00 sec
----------------------------------------------------------
Test Failed.
"FloatTest.FailureTestForSimpleCalculation" end time: Aug 05 19:11 JST
"FloatTest.FailureTestForSimpleCalculation" time elapsed: 00:00:00
----------------------------------------------------------
```

# 単純な演算であればEXPECT_FLOAT_EQが使える

先ほど失敗したテストは、**EXPECT_FLOAT_EQ(ASSERT_FLOAT_EQ)**を使えば等しいと判断されます。
**EXPECT_FLOAT_EQ**は、4ULPs（1ULPs:float型の最下位桁数単位）を基準に、２値の差が許容できる誤差範囲かを判定します。
[公式ドキュメント](http://opencv.jp/googletestdocs/advancedguide.html#adv-floating-point-comparision)
[浮動小数点数の誤差について詳しく知りたい方はこちら](http://www.cc.kyoto-su.ac.jp/~yamada/programming/float.html#float)

```cpp:test_float.cpp
TEST(FloatTest, SuccessfulTestForSimpleCalculation){

  float a, b;

  a =  555555.5;
  b =  0.055555;

  EXPECT_FLOAT_EQ(555555.555555, a+b); //成功する
}
```
```terminal:テストログ
2/4 Testing: FloatTest.SuccessfulTestForSimpleCalculation
2/4 Test: FloatTest.SuccessfulTestForSimpleCalculation
Command: "/.../build/test/AllTests" "--gtest_filter=FloatTest.SuccessfulTestForSimpleCalculation"
Directory: /.../build/test
"FloatTest.SuccessfulTestForSimpleCalculation" start time: Aug 05 19:11 JST
Output:
----------------------------------------------------------
Running main() from /.../src/gtest_main.cc
Note: Google Test filter = FloatTest.SuccessfulTestForSimpleCalculation
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from FloatTest
[ RUN      ] FloatTest.SuccessfulTestForSimpleCalculation
[       OK ] FloatTest.SuccessfulTestForSimpleCalculation (0 ms)
[----------] 1 test from FloatTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.
<end of output>
Test time =   0.00 sec
----------------------------------------------------------
Test Passed.
"FloatTest.SuccessfulTestForSimpleCalculation" end time: Aug 05 19:11 JST
"FloatTest.SuccessfulTestForSimpleCalculation" time elapsed: 00:00:00
----------------------------------------------------------

```
# 丸め誤差が発生する演算には、EXPECT_NEARを使おう

**EXPECT_FLOAT_EQ(ASSERT_FLOAT_EQ)**は、繰り返し演算によって発生する、情報落ちには対応できないため、
下記のようなテストは失敗します。

```cpp:test_float.cpp
TEST(FloatTest, FailureTestForLossOfTrailingDigits){

  float a, b;

  a =  155000.0;
  b =  0.550000;

  //a=100000.0になることを期待した演算
  for(int i=0; i<100000; i++){
    a -= b;
  }

  EXPECT_FLOAT_EQ(100000.0, a); //失敗する
}
```

```terminal:テストログ
3/4 Testing: FloatTest.FailureTestForLossOfTrailingDigits
3/4 Test: FloatTest.FailureTestForLossOfTrailingDigits
Command: ".../build/test/AllTests" "--gtest_filter=FloatTest.FailureTestForLossOfTrailingDigits"
Directory: .../build/test
"FloatTest.FailureTestForLossOfTrailingDigits" start time: Aug 05 19:11 JST
Output:
----------------------------------------------------------
Running main() from /.../src/gtest_main.cc
Note: Google Test filter = FloatTest.FailureTestForLossOfTrailingDigits
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from FloatTest
[ RUN      ] FloatTest.FailureTestForLossOfTrailingDigits
/.../test/test_float.cpp:35: Failure
Expected equality of these values:
  100000.0
    Which is: 100000
  a
    Which is: 100312.5
[  FAILED  ] FloatTest.FailureTestForLossOfTrailingDigits (0 ms)
[----------] 1 test from FloatTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] FloatTest.FailureTestForLossOfTrailingDigits

 1 FAILED TEST
<end of output>
Test time =   0.00 sec
----------------------------------------------------------
Test Failed.
"FloatTest.FailureTestForLossOfTrailingDigits" end time: Aug 05 19:11 JST
"FloatTest.FailureTestForLossOfTrailingDigits" time elapsed: 00:00:00
----------------------------------------------------------
```

上記例では、真値に対して312.5もの誤差が発生しています。
丸め誤差を考慮したテストを記述する場合は、**EXPECT_NEAR(ASSERT_NEAR)**を使うと、許容誤差範囲を引数によって指定することができます。

```cpp:test_float.cpp
TEST(FloatTest, SuccessfulTestForLossOfTrailingDigits){

  float a, b;

  a =  155000.0;
  b =  0.550000;

  //a=100000.0になることを期待した演算
  for(int i=0; i<100000; i++){
    a -= b;
  }

  EXPECT_NEAR(1.0e+5, a, 5.0e+2); //成功する
}
```

```terminal:テストログ
4/4 Testing: FloatTest.SuccessfulTestForLossOfTrailingDigits
4/4 Test: FloatTest.SuccessfulTestForLossOfTrailingDigits
Command: "/.../build/test/AllTests" "--gtest_filter=FloatTest.SuccessfulTestForLossOfTrailingDigits"
Directory: /.../build/test
"FloatTest.SuccessfulTestForLossOfTrailingDigits" start time: Aug 05 19:11 JST
Output:
----------------------------------------------------------
Running main() from /.../src/gtest_main.cc
Note: Google Test filter = FloatTest.SuccessfulTestForLossOfTrailingDigits
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from FloatTest
[ RUN      ] FloatTest.SuccessfulTestForLossOfTrailingDigits
[       OK ] FloatTest.SuccessfulTestForLossOfTrailingDigits (1 ms)
[----------] 1 test from FloatTest (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[  PASSED  ] 1 test.
<end of output>
Test time =   0.00 sec
----------------------------------------------------------
Test Passed.
"FloatTest.SuccessfulTestForLossOfTrailingDigits" end time: Aug 05 19:11 JST
"FloatTest.SuccessfulTestForLossOfTrailingDigits" time elapsed: 00:00:00
----------------------------------------------------------

End testing: Aug 05 19:11 JST
```

#まとめ

- 浮動小数数の比較に**EXPECT_EQ**は使えない
- 単純な演算であれば**EXPECT_FLOAT_EQ**が使える
- 丸め誤差が発生する演算には**EXPECT_NEAR**を使おう

#参考文献
http://www.cc.kyoto-su.ac.jp/~yamada/programming/float.html#float
http://opencv.jp/googletestdocs/index.html
https://qiita.com/tobira-code/items/d9c40b650dec40d3c2ed