/** * 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