/**
* Copyright (C) 2016 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#include "mongo/platform/basic.h"
#include
#include
#include
#include "mongo/unittest/unittest.h"
#include "mongo/util/represent_as.h"
namespace mongo {
namespace {
// Char values
const signed char kCharMax = std::numeric_limits::max();
const int kCharMaxAsInt = kCharMax;
// Unsigned char values
const unsigned char kUCharMax = std::numeric_limits::max();
const unsigned char kUCharMin = std::numeric_limits::lowest();
const int kUCharMaxAsInt = kUCharMax;
// Int values
const int kIntMax = std::numeric_limits::max();
const int kIntMin = std::numeric_limits::lowest();
const long long kIntMaxAsLongLong = kIntMax;
const long long kIntMinAsLongLong = kIntMin;
const unsigned long long kIntMaxAsULongLong = kIntMax;
const unsigned long long kIntMinAsULongLong = kIntMin;
// 32-bit integer values
const int32_t kInt32Zero = 0;
const int32_t kInt32Max = std::numeric_limits::max();
const int32_t kInt32Min = std::numeric_limits::lowest();
const uint32_t kInt32MaxAsUInt32 = kInt32Max;
const uint64_t kInt32MaxAsUInt64 = kInt32Max;
const double kInt32MaxAsDouble = kInt32Max;
const double kInt32MinAsDouble = kInt32Min;
// Unsigned 32-bit integer values
const uint32_t kUInt32Zero = 0;
const uint32_t kUInt32Max = std::numeric_limits::max();
const int64_t kUInt32MaxAsInt64 = kUInt32Max;
const float kUInt32MaxAsFloat = static_cast(kUInt32Max);
const double kUInt32MaxAsDouble = kUInt32Max;
// 64-bit integer values
const int64_t kInt64Zero = 0;
const int64_t kInt64Max = std::numeric_limits::max();
const int64_t kInt64Min = std::numeric_limits::lowest();
const uint64_t kInt64MaxAsUInt64 = kInt64Max;
const double kInt64MaxAsDouble = static_cast(kInt64Max);
const double kInt64MinAsDouble = kInt64Min;
// Unsigned 64-bit integer values
const uint64_t kUInt64Zero = 0;
const uint64_t kUInt64Max = std::numeric_limits::max();
const float kUInt64MaxAsFloat = static_cast(kUInt64Max);
const double kUInt64MaxAsDouble = static_cast(kUInt64Max);
// Long long values
const long long kLongLongMax = std::numeric_limits::max();
// Unsigned long long values
const unsigned long long kULongLongMax = std::numeric_limits::max();
// Float values
const float kFloatZero = 0;
const float kFloatMax = std::numeric_limits::max();
const float kFloatMin = std::numeric_limits::lowest();
const double kFloatMaxAsDouble = kFloatMax;
const double kFloatMinAsDouble = kFloatMin;
// Double values
const double kDoubleZero = 0;
const double kDoubleMax = std::numeric_limits::max();
const double kDoubleMin = std::numeric_limits::lowest();
// Precision values
const int kFloatMantissa = std::numeric_limits::digits;
const int kDoubleMantissa = std::numeric_limits::digits;
const int32_t kInt32TooPreciseForFloat =
static_cast(std::ldexp(1, kFloatMantissa + 1)) + 1;
const uint32_t kUInt32TooPreciseForFloat = kInt32TooPreciseForFloat;
const int64_t kInt64TooPreciseForFloat = kInt32TooPreciseForFloat;
const int64_t kInt64TooPreciseForDouble =
static_cast(std::ldexp(1, kDoubleMantissa + 1)) + 1;
const uint64_t kUInt64TooPreciseForFloat = kInt32TooPreciseForFloat;
const uint64_t kUInt64TooPreciseForDouble = kInt64TooPreciseForDouble;
} // namespace
TEST(RepresentAs, Int32ToDouble) {
ASSERT(*(representAs(kInt32Zero)) == 0);
ASSERT(*(representAs(5)) == 5);
}
TEST(RepresentAs, Int64ToDouble) {
ASSERT(*(representAs(kInt64Zero)) == 0);
ASSERT(*(representAs(5)) == 5);
// kInt64Max is too precise for double
ASSERT(!(representAs(kInt64Max)));
ASSERT(*(representAs(kInt64Min)) == kInt64MinAsDouble);
}
TEST(RepresentAs, DoubleToInt32) {
ASSERT(*(representAs(kDoubleZero)) == 0);
ASSERT(*(representAs(-12345)) == -12345);
ASSERT(!(representAs(10.3)));
// Int32 edge cases
ASSERT(*(representAs(kInt32Max)) == kInt32Max);
ASSERT(!(representAs(kInt32MaxAsDouble + 1)));
ASSERT(*(representAs(kInt32Min)) == kInt32Min);
ASSERT(!(representAs(kInt32MinAsDouble - 1)));
// Very large and small values
ASSERT(!(representAs(kDoubleMax)));
ASSERT(!(representAs(kDoubleMin)));
}
TEST(RepresentAs, DoubleToInt64) {
ASSERT(*(representAs(kDoubleZero)) == 0);
ASSERT(*(representAs(-12345)) == -12345);
ASSERT(!(representAs(10.3)));
// Int64 edge cases, max can't be represented as doubles, min can
ASSERT(!(representAs(kInt64MaxAsDouble)));
ASSERT(*(representAs(kInt64MinAsDouble)) == kInt64Min);
// Very large and small values
ASSERT(!(representAs(kDoubleMax)));
ASSERT(!(representAs(kDoubleMin)));
}
TEST(RepresentAs, DoubleToFloat) {
ASSERT(*(representAs(kDoubleZero)) == 0);
ASSERT(*(representAs(-12345)) == -12345);
// Float edge casees
ASSERT(*(representAs(kFloatMax)) == (representAs(kFloatMaxAsDouble + 1)));
ASSERT(*(representAs(kFloatMin)) == (representAs(kFloatMinAsDouble - 1)));
// Very large and small values
ASSERT(!(representAs(kDoubleMax)));
ASSERT(!(representAs(kDoubleMin)));
}
TEST(RepresentAs, DoubleToUnsignedInt) {
ASSERT(!(representAs(-1.23)));
ASSERT(*(representAs(kDoubleZero)) == kUInt64Zero);
ASSERT(!(representAs(kDoubleMax)));
ASSERT(!(representAs(kDoubleMax)));
}
TEST(RepresentAs, FloatToDouble) {
ASSERT(*(representAs(kFloatZero)) == 0);
ASSERT(*(representAs(-12345)) == -12345);
ASSERT(*(representAs(kFloatMax)) == kFloatMax);
ASSERT(*(representAs(kFloatMin)) == kFloatMin);
}
TEST(RepresentAs, FloatToUnsignedInt) {
ASSERT(!(representAs(-1.23)));
ASSERT(!(representAs(-1)));
ASSERT(*(representAs(kUInt64Zero)) == kUInt64Zero);
ASSERT(*(representAs(10)) == static_cast(10));
ASSERT(!(representAs(kFloatMax)));
ASSERT(!(representAs(kFloatMax)));
}
TEST(RepresentAs, SignedAndUnsigned32BitIntegers) {
ASSERT(!(representAs(kInt32Min)));
ASSERT(*(representAs(kInt32Max)) == kInt32MaxAsUInt32);
ASSERT(!(representAs(kUInt32Max)));
ASSERT(!(representAs(kInt32MaxAsUInt32 + 1)));
}
TEST(RepresentAs, SignedAndUnsigned64BitIntegers) {
ASSERT(!(representAs(kInt64Min)));
ASSERT(*(representAs(kInt64Max)) == kInt64MaxAsUInt64);
ASSERT(!(representAs(kUInt64Max)));
ASSERT(!(representAs(kInt64MaxAsUInt64 + 1)));
}
TEST(RepresentAs, SignedAndUnsignedMixedSizeIntegers) {
ASSERT(!(representAs(kInt64Min)));
ASSERT(!(representAs(kInt64Max)));
ASSERT(*(representAs(kUInt32Max)) == kUInt32MaxAsInt64);
ASSERT(!(representAs(kInt32Min)));
ASSERT(*(representAs(kInt32Max)) == kInt32MaxAsUInt64);
ASSERT(!(representAs(kUInt64Max)));
}
TEST(RepresentAs, UnsignedIntToFloat) {
// kUInt32Max and kUInt64Max are too precise for float.
ASSERT(!(representAs(kUInt32Max)));
ASSERT(!(representAs(kUInt64Max)));
}
TEST(RepresentAs, UnsignedIntToDouble) {
// kUInt64Max is too precise for double.
ASSERT(*(representAs(kUInt32Max)) == kUInt32MaxAsDouble);
ASSERT(!(representAs(kUInt64Max)));
}
TEST(RepresentAs, PlatformDependent) {
// signed char
ASSERT(*(representAs(kCharMax)) == kCharMaxAsInt);
ASSERT(!(representAs(kIntMax)));
// unsigned char
ASSERT(*(representAs(kUCharMax)) == kUCharMaxAsInt);
ASSERT(!(representAs(kIntMin)));
// long long
ASSERT(!(representAs(kLongLongMax)));
ASSERT(*(representAs(kIntMin)) == kIntMinAsLongLong);
// unsigned long long
ASSERT(!(representAs(kULongLongMax)));
ASSERT(*(representAs(kIntMax)) == kIntMaxAsULongLong);
}
TEST(RepresentAs, NaN) {
ASSERT(!(representAs(std::nanf("1"))));
ASSERT(!(representAs(std::nanf("1"))));
// NaN Identities
ASSERT(std::isnan(*representAs(std::nanf("1"))));
ASSERT(std::isnan(*representAs(std::nanf("1"))));
ASSERT(std::isnan(*representAs(std::nan("1"))));
ASSERT(std::isnan(*representAs(std::nan("1"))));
}
TEST(RepresentAs, LostPrecision) {
// A loss of precision should result in a disengaged optional
ASSERT(!(representAs(kInt32TooPreciseForFloat)));
ASSERT(!(representAs(kUInt32TooPreciseForFloat)));
ASSERT(!(representAs(kInt64TooPreciseForFloat)));
ASSERT(!(representAs(kUInt64TooPreciseForFloat)));
ASSERT(!(representAs(kInt64TooPreciseForDouble)));
ASSERT(!(representAs(kUInt64TooPreciseForDouble)));
}
TEST(RepresentAs, Identity) {
ASSERT(*(representAs(kInt32Max)) == kInt32Max);
ASSERT(*(representAs(kInt64Max)) == kInt64Max);
ASSERT(*(representAs(50)) == 50);
ASSERT(*(representAs(kFloatMin)) == kFloatMin);
ASSERT(*(representAs(kDoubleMax)) == kDoubleMax);
ASSERT(*(representAs(kUInt32Max)) == kUInt32Max);
ASSERT(*(representAs(kUInt64Max)) == kUInt64Max);
}
} // namespace mongo