Skip to content

Commit

Permalink
Handle range checking of floating point numbers correctly.
Browse files Browse the repository at this point in the history
Underlying rapidjson::IsLosslessFloat() does not work correctly
(e.g. IsLosslessFloat(0.2) returns false!). We now split the
range check into two parts: one to check if a integral value
can fit in float/double without loss and other part to do usual
decimal compare against the defined limits. The latter is used
for non-integer values.

Fixes #1136

PiperOrigin-RevId: 217166796
  • Loading branch information
netfs authored and tensorflower-gardener committed Oct 15, 2018
1 parent 1cceb78 commit 01c2fb8
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 13 deletions.
18 changes: 15 additions & 3 deletions tensorflow_serving/util/json_tensor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.

#include "tensorflow_serving/util/json_tensor.h"

#include <cstdlib>
#include <limits>
#include <string>
#include <type_traits>
Expand Down Expand Up @@ -209,15 +210,26 @@ Status LossyDecimalError(const rapidjson::Value& val, const string& target) {
template <typename T>
bool IsLosslessDecimal(const rapidjson::Value& val) {
static_assert(std::is_same<T, float>::value || std::is_same<T, double>::value,
"Only floating-point value types are supported.");
"Only floating point value types are supported.");
static_assert(std::numeric_limits<T>::radix == 2,
"Floating point type must have base 2.");

// Note, we use GetDouble() for both types as std::isfinite() returns false
// for decimal values that do not fit in float (due to the static_cast<> used
// in converting double to float in GetFloat() call).
if (!std::isfinite(val.GetDouble())) return true;

if (std::is_same<T, float>::value) return val.IsLosslessFloat();
return val.IsLosslessDouble();
// Maximum integer value that can be represented by the floating type.
static constexpr int64 kMaxInt = (1LL << std::numeric_limits<T>::digits) - 1;

if (val.IsUint64()) {
return val.GetUint64() <= kMaxInt;
}
if (val.IsInt64()) {
return std::abs(val.GetInt64()) <= kMaxInt;
}
return val.GetDouble() <= std::numeric_limits<T>::max() &&
val.GetDouble() >= std::numeric_limits<T>::lowest();
}

// Adds a JSON value to Tensor. Returns error if value cannot be converted
Expand Down
24 changes: 14 additions & 10 deletions tensorflow_serving/util/json_tensor_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ TEST(JsontensorTest, SingleUnnamedTensor) {
)"));
}

TEST(JsontensorTest, IntegerInputForFloatTensor) {
TEST(JsontensorTest, MixedInputForFloatTensor) {
TensorInfoMap infomap;
ASSERT_TRUE(
TextFormat::ParseFromString("dtype: DT_FLOAT", &infomap["default"]));
Expand All @@ -90,24 +90,26 @@ TEST(JsontensorTest, IntegerInputForFloatTensor) {
JsonPredictRequestFormat format;
TF_EXPECT_OK(FillPredictRequestFromJson(R"(
{
"instances": [1, 2, 3, 4, 5]
"instances": [1, 2.0, 3, 4, 5.003, 0.007, 0.0]
})",
getmap(infomap), &req, &format));
auto tmap = req.inputs();
EXPECT_EQ(tmap.size(), 1);
EXPECT_EQ(format, JsonPredictRequestFormat::kRow);
EXPECT_THAT(tmap["default"], EqualsProto(R"(
dtype: DT_FLOAT
tensor_shape { dim { size: 5 } }
tensor_shape { dim { size: 7 } }
float_val: 1
float_val: 2
float_val: 2.0
float_val: 3
float_val: 4
float_val: 5
float_val: 5.003
float_val: 0.007
float_val: 0.0
)"));
}

TEST(JsontensorTest, IntegerInputForDoubleTensor) {
TEST(JsontensorTest, MixedInputForDoubleTensor) {
TensorInfoMap infomap;
ASSERT_TRUE(
TextFormat::ParseFromString("dtype: DT_DOUBLE", &infomap["default"]));
Expand All @@ -116,20 +118,22 @@ TEST(JsontensorTest, IntegerInputForDoubleTensor) {
JsonPredictRequestFormat format;
TF_EXPECT_OK(FillPredictRequestFromJson(R"(
{
"instances": [1, 2, 3, 4, 5]
"instances": [1.0, 2, 3, 4, 0.662, 0, 0.0]
})",
getmap(infomap), &req, &format));
auto tmap = req.inputs();
EXPECT_EQ(tmap.size(), 1);
EXPECT_EQ(format, JsonPredictRequestFormat::kRow);
EXPECT_THAT(tmap["default"], EqualsProto(R"(
dtype: DT_DOUBLE
tensor_shape { dim { size: 5 } }
double_val: 1
tensor_shape { dim { size: 7 } }
double_val: 1.0
double_val: 2
double_val: 3
double_val: 4
double_val: 5
double_val: 0.662
double_val: 0
double_val: 0.0
)"));
}

Expand Down

0 comments on commit 01c2fb8

Please sign in to comment.