Skip to content

Commit

Permalink
ENH: Support calling a const NumberToString, add GoogleTest unit tests
Browse files Browse the repository at this point in the history
Supported calling a `const` NumberToString function object, by declaring
all `NumberToString::operator()` overloads `const`.

Added various GoogleTest unit tests, which do call `const`
NumberToString objects.
  • Loading branch information
N-Dekker authored and dzenanz committed Jan 18, 2021
1 parent 3199260 commit f3b3f69
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 6 deletions.
6 changes: 3 additions & 3 deletions Modules/Core/Common/include/itkNumberToString.h
Expand Up @@ -43,16 +43,16 @@ class ITK_TEMPLATE_EXPORT NumberToString
{
public:
std::string
operator()(TValue val);
operator()(TValue val) const;
};

// declaration of specialization
template <>
ITKCommon_EXPORT std::string
NumberToString<double>::operator()(double val);
NumberToString<double>::operator()(double val) const;
template <>
ITKCommon_EXPORT std::string
NumberToString<float>::operator()(float val);
NumberToString<float>::operator()(float val) const;

} // namespace itk

Expand Down
2 changes: 1 addition & 1 deletion Modules/Core/Common/include/itkNumberToString.hxx
Expand Up @@ -28,7 +28,7 @@ namespace itk

template <typename TValue>
std::string
NumberToString<TValue>::operator()(TValue val)
NumberToString<TValue>::operator()(TValue val) const
{
std::ostringstream output;
output << static_cast<typename NumericTraits<TValue>::PrintType>(val);
Expand Down
4 changes: 2 additions & 2 deletions Modules/Core/Common/src/itkNumberToString.cxx
Expand Up @@ -26,7 +26,7 @@ namespace itk

template <>
std::string
NumberToString<double>::operator()(double val)
NumberToString<double>::operator()(double val) const
{
char buf[256];
const double_conversion::DoubleToStringConverter & converter =
Expand All @@ -42,7 +42,7 @@ NumberToString<double>::operator()(double val)

template <>
std::string
NumberToString<float>::operator()(float val)
NumberToString<float>::operator()(float val) const
{
char buf[256];
const double_conversion::DoubleToStringConverter & converter =
Expand Down
1 change: 1 addition & 0 deletions Modules/Core/Common/test/CMakeLists.txt
Expand Up @@ -617,6 +617,7 @@ set(ITKCommonGTests
itkIndexRangeGTest.cxx
itkMersenneTwisterRandomVariateGeneratorGTest.cxx
itkNeighborhoodAllocatorGTest.cxx
itkNumberToStringGTest.cxx
itkOptimizerParametersGTest.cxx
itkPointGTest.cxx
itkShapedImageNeighborhoodRangeGTest.cxx
Expand Down
144 changes: 144 additions & 0 deletions Modules/Core/Common/test/itkNumberToStringGTest.cxx
@@ -0,0 +1,144 @@
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/

// First include the header file to be tested:
#include "itkNumberToString.h"
#include <gtest/gtest.h>

namespace
{

template <typename TValue>
void
Test_all_digits()
{
const itk::NumberToString<TValue> numberToString{};

for (auto i = 9; i >= 0; --i)
{
EXPECT_EQ(numberToString(i), std::to_string(i));
}
}


template <typename TValue>
void
Test_non_finite_special_floating_point_values()
{
using NumericLimitsType = std::numeric_limits<TValue>;
const itk::NumberToString<TValue> numberToString{};

EXPECT_EQ(numberToString(NumericLimitsType::quiet_NaN()), "NaN");
EXPECT_EQ(numberToString(NumericLimitsType::infinity()), "Infinity");
EXPECT_EQ(numberToString(-NumericLimitsType::infinity()), "-Infinity");
}


template <typename TValue>
void
Test_round_trip_of_finite_numeric_limits()
{
using NumericLimitsType = std::numeric_limits<TValue>;
const itk::NumberToString<TValue> numberToString{};

for (const TValue expectedValue : { NumericLimitsType::lowest(),
NumericLimitsType::epsilon(),
NumericLimitsType::round_error(),
NumericLimitsType::denorm_min(),
NumericLimitsType::min(),
NumericLimitsType::max() })
{
std::istringstream inputStream{ numberToString(expectedValue) };
EXPECT_FALSE(inputStream.eof());
TValue actualValue;
inputStream >> actualValue;
EXPECT_TRUE(inputStream.eof());
EXPECT_EQ(actualValue, expectedValue);
}
}


template <typename TValue>
void
Test_decimal_notation_supports_up_to_twentyone_digits()
{
const itk::NumberToString<TValue> numberToString{};

for (std::int8_t exponent{ 20 }; exponent > 0; --exponent)
{
const auto power_of_ten = std::pow(TValue{ 10 }, static_cast<TValue>(exponent));

// Test +/- 10 ^ exponent
EXPECT_EQ(numberToString(power_of_ten), '1' + std::string(exponent, '0'));
EXPECT_EQ(numberToString(-power_of_ten), "-1" + std::string(exponent, '0'));
}

for (std::int8_t exponent{ -6 }; exponent < 0; ++exponent)
{
const auto power_of_ten = std::pow(TValue{ 10 }, static_cast<TValue>(exponent));

// Test +/- 10 ^ exponent
EXPECT_EQ(numberToString(power_of_ten), "0." + std::string(-1 - exponent, '0') + '1');
EXPECT_EQ(numberToString(-power_of_ten), "-0." + std::string(-1 - exponent, '0') + '1');
}
}

} // namespace


// Tests NumberToString for 0 to 9.
TEST(NumberToString, SupportsAllDigits)
{
Test_all_digits<int>();
Test_all_digits<float>();
Test_all_digits<double>();
}


// Tests that the function object returns a unique string for both positive and negative zero.
TEST(NumberToString, HasUniqueZeroString)
{
const std::string expectedZeroString = "0";

EXPECT_EQ(itk::NumberToString<int>{}(0), expectedZeroString);
EXPECT_EQ(itk::NumberToString<float>{}(+0.0f), expectedZeroString);
EXPECT_EQ(itk::NumberToString<float>{}(-0.0f), expectedZeroString);
EXPECT_EQ(itk::NumberToString<double>{}(+0.0), expectedZeroString);
EXPECT_EQ(itk::NumberToString<double>{}(-0.0), expectedZeroString);
}


TEST(NumberToString, NonFiniteSpecialFloatingPointValues)
{
Test_non_finite_special_floating_point_values<float>();
Test_non_finite_special_floating_point_values<double>();
}


TEST(NumberToString, RoundTripOfFiniteFloatingPointNumericLimits)
{
Test_round_trip_of_finite_numeric_limits<float>();
Test_round_trip_of_finite_numeric_limits<double>();
}


TEST(NumberToString, DecimalNotationUpTo21Digits)
{
Test_decimal_notation_supports_up_to_twentyone_digits<float>();
Test_decimal_notation_supports_up_to_twentyone_digits<double>();
}

0 comments on commit f3b3f69

Please sign in to comment.