Skip to content

Commit acd80ab

Browse files
author
lexeyo
committed
fix utils: proper handling of infinite floating point values in numeric_cast
Fixes numeric\_cast of infinite floating type values. Adds extra numeric\_cast tests for infinte and NaN values. commit_hash:580336e395481c234735d78da6cdbfee2a58d10a
1 parent e304e4b commit acd80ab

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

universal/include/userver/utils/numeric_cast.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/// @file userver/utils/numeric_cast.hpp
44
/// @brief @copybrief utils::numeric_cast
55

6+
#include <cmath>
67
#include <limits>
78
#include <stdexcept>
89

@@ -50,13 +51,17 @@ constexpr To numeric_cast(From input) { // NOLINT(readability-identifier-naming
5051

5152
if constexpr (check_positive_overflow) {
5253
if (input > static_cast<From>(ToLimits::max())) {
53-
overflow_type = "positive";
54+
if (!FromLimits::has_infinity || !std::isinf(input)) {
55+
overflow_type = "positive";
56+
}
5457
}
5558
}
5659

5760
if constexpr (check_negative_overflow) {
5861
if (input < static_cast<From>(ToLimits::lowest())) {
59-
overflow_type = "negative";
62+
if (!FromLimits::has_infinity || !std::isinf(input)) {
63+
overflow_type = "negative";
64+
}
6065
}
6166
}
6267

universal/src/utils/numeric_cast_test.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
#include <cmath>
12
#include <limits>
23

34
#include <gmock/gmock.h>
5+
#include <gtest/gtest.h>
46
#include <userver/utest/assert_macros.hpp>
57

68
#include <userver/utils/numeric_cast.hpp>
@@ -9,6 +11,29 @@ USERVER_NAMESPACE_BEGIN
911

1012
namespace {
1113

14+
template <typename TFrom, typename TTo>
15+
struct CastTypes {
16+
using From = TFrom;
17+
using To = TTo;
18+
};
19+
20+
using FloatingPointCastTypes = ::testing::Types<
21+
CastTypes<float, float>,
22+
CastTypes<float, double>,
23+
CastTypes<float, long double>,
24+
CastTypes<double, float>,
25+
CastTypes<double, double>,
26+
CastTypes<double, long double>,
27+
CastTypes<long double, float>,
28+
CastTypes<long double, double>,
29+
CastTypes<long double, long double>>;
30+
31+
template <typename CastTypes>
32+
class NumericCastFloatingPoint : public ::testing::Test {
33+
using From = typename CastTypes::From;
34+
using To = typename CastTypes::To;
35+
};
36+
1237
TEST(NumericCast, CompileTime) {
1338
static_assert(utils::numeric_cast<unsigned int>(1) == 1u);
1439
static_assert(utils::numeric_cast<float>(1.0) == 1.0f);
@@ -33,6 +58,29 @@ TEST(NumericCast, Smoke) {
3358
EXPECT_EQ(utils::numeric_cast<long double>(1.0L), 1.0L);
3459
}
3560

61+
TYPED_TEST_SUITE(NumericCastFloatingPoint, FloatingPointCastTypes);
62+
63+
TYPED_TEST(NumericCastFloatingPoint, Nan) {
64+
using Limits = std::numeric_limits<typename TypeParam::From>;
65+
66+
if constexpr (Limits::has_quiet_NaN) {
67+
EXPECT_TRUE(std::isnan(utils::numeric_cast<typename TypeParam::To>(Limits::quiet_NaN())));
68+
}
69+
}
70+
71+
TYPED_TEST(NumericCastFloatingPoint, Infinity) {
72+
using To = typename TypeParam::To;
73+
using FromLimits = std::numeric_limits<typename TypeParam::From>;
74+
using ToLimits = std::numeric_limits<To>;
75+
76+
if constexpr (FromLimits::has_infinity) {
77+
// +inf
78+
EXPECT_EQ(utils::numeric_cast<To>(FromLimits::infinity()), ToLimits::infinity());
79+
// -inf
80+
EXPECT_EQ(utils::numeric_cast<To>(-FromLimits::infinity()), -ToLimits::infinity());
81+
}
82+
}
83+
3684
TEST(NumericCast, SignedToUnsignedOverflow) {
3785
/// [Sample utils::numeric_cast usage]
3886
EXPECT_EQ(utils::numeric_cast<std::uint16_t>(0xffff), 0xffffu);

0 commit comments

Comments
 (0)