Skip to content

Commit d3fa66c

Browse files
author
pananton
committed
fix userver: fix double to float conversion for 'Value' classes
Relates: <https://nda.ya.ru/t/Dx1JoAAr7M6sew> commit_hash:62bb808e44373deaf2d19bda87f72a92c6072f35
1 parent 35540ed commit d3fa66c

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

universal/include/userver/formats/parse/common.hpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
/// @ingroup userver_universal userver_formats_parse
88

99
#include <chrono>
10+
#include <cmath>
1011
#include <cstdint>
1112
#include <limits>
1213

@@ -34,9 +35,25 @@ void CheckInBounds(const Value& value, T x, T min, T max) {
3435
}
3536
}
3637

38+
template <typename Value>
39+
void CheckDoubleFitsInFloat(const Value& value, const double dval) {
40+
if (std::isinf(dval) || std::isnan(dval)) {
41+
// double infinity and NaN are directly convertible to float infinity and NaN
42+
return;
43+
}
44+
45+
auto fval = static_cast<float>(dval);
46+
47+
if (!std::isfinite(fval)) {
48+
throw typename Value::ParseException(
49+
fmt::format("Double value ({}) of '{}' does not fit into float", dval, value.GetPath())
50+
);
51+
}
52+
}
53+
3754
template <typename Value>
3855
float NarrowToFloat(double x, const Value& value) {
39-
CheckInBounds<double>(value, x, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
56+
CheckDoubleFitsInFloat(value, x);
4057
return static_cast<float>(x);
4158
}
4259

universal/src/formats/common/value_test.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
#include <userver/formats/common/type.hpp>
12
#include <userver/formats/parse/boost_flat_containers.hpp>
23
#include <userver/formats/parse/boost_optional.hpp>
34
#include <userver/formats/parse/time_of_day.hpp>
45
#include <userver/formats/parse/to.hpp>
56
#include <userver/formats/parse/variant.hpp>
7+
#include <userver/utils/meta_light.hpp>
68

9+
#include <limits>
710
#include <map>
811
#include <optional>
912
#include <set>
13+
#include <stdexcept>
1014
#include <string>
1115
#include <unordered_map>
1216
#include <unordered_set>
@@ -199,6 +203,23 @@ TYPED_TEST_P(Parsing, IntOverflow) {
199203
EXPECT_EQ(65535, value.template As<uint16_t>());
200204
}
201205

206+
TYPED_TEST_P(Parsing, MaxMinFloat) {
207+
using Value = std::decay_t<decltype(this->kFromString(""))>;
208+
209+
constexpr float kFloatMax = std::numeric_limits<float>::max();
210+
constexpr float kFloatMin = std::numeric_limits<float>::min();
211+
212+
auto float_max = this->kFromString(fmt::format("[{}]", kFloatMax))[0];
213+
auto float_negative_max = this->kFromString(fmt::format("[{}]", -kFloatMax))[0];
214+
auto float_min = this->kFromString(fmt::format("[{}]", kFloatMin))[0];
215+
auto float_negative_min = this->kFromString(fmt::format("[{}]", -kFloatMin))[0];
216+
217+
EXPECT_EQ(float_max.template As<float>(), kFloatMax);
218+
EXPECT_EQ(float_negative_max.template As<float>(), -kFloatMax);
219+
EXPECT_EQ(float_min.template As<float>(), kFloatMin);
220+
EXPECT_EQ(float_negative_min.template As<float>(), -kFloatMin);
221+
}
222+
202223
TYPED_TEST_P(Parsing, UserProvidedCommonParser) {
203224
auto value = this->kFromString("[42]")[0];
204225

@@ -372,6 +393,7 @@ REGISTER_TYPED_TEST_SUITE_P(
372393
Int,
373394
UInt,
374395
IntOverflow,
396+
MaxMinFloat,
375397
UserProvidedCommonParser,
376398

377399
ChronoDoubleSeconds,

0 commit comments

Comments
 (0)