// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Tests for Emboss floating-point support. #include #include #include #include "gtest/gtest.h" #include "testdata/float.emb.h" namespace emboss { namespace test { namespace { ::std::array MakeFloats(::std::uint32_t bits) { return ::std::array({{ // Little endian version static_cast(bits & 0xff), // static_cast((bits >> 8) & 0xff), // static_cast((bits >> 16) & 0xff), // static_cast((bits >> 24) & 0xff), // // Big endian version static_cast((bits >> 24) & 0xff), // static_cast((bits >> 16) & 0xff), // static_cast((bits >> 8) & 0xff), // static_cast(bits & 0xff), // }}); } ::std::array MakeDoubles(::std::uint64_t bits) { return ::std::array({{ // Little endian version static_cast(bits & 0xff), // static_cast((bits >> 8) & 0xff), // static_cast((bits >> 16) & 0xff), // static_cast((bits >> 24) & 0xff), // static_cast((bits >> 32) & 0xff), // static_cast((bits >> 40) & 0xff), // static_cast((bits >> 48) & 0xff), // static_cast((bits >> 56) & 0xff), // // Big endian version static_cast((bits >> 56) & 0xff), // static_cast((bits >> 48) & 0xff), // static_cast((bits >> 40) & 0xff), // static_cast((bits >> 32) & 0xff), // static_cast((bits >> 24) & 0xff), // static_cast((bits >> 16) & 0xff), // static_cast((bits >> 8) & 0xff), // static_cast(bits & 0xff), // }}); } // This is used separately for tests where !(a == a). void TestFloatWrite(float value, ::std::uint32_t bits) { const auto floats = MakeFloats(bits); ::std::array buffer = {}; auto writer = MakeFloatsView(&buffer); EXPECT_TRUE(writer.float_little_endian().CouldWriteValue(value)); EXPECT_TRUE(writer.float_big_endian().CouldWriteValue(value)); writer.float_little_endian().Write(value); writer.float_big_endian().Write(value); EXPECT_EQ(floats, buffer); } ::std::array TestFloatValue(float value, ::std::uint32_t bits) { const auto floats = MakeFloats(bits); auto view = MakeFloatsView(&floats); EXPECT_EQ(value, view.float_little_endian().Read()); EXPECT_EQ(value, view.float_big_endian().Read()); TestFloatWrite(value, bits); return floats; } // This is used separately for tests where !(a == a). void TestDoubleWrite(double value, ::std::uint64_t bits) { const auto doubles = MakeDoubles(bits); ::std::array buffer = {}; auto writer = MakeDoublesView(&buffer); EXPECT_TRUE(writer.double_little_endian().CouldWriteValue(value)); EXPECT_TRUE(writer.double_big_endian().CouldWriteValue(value)); writer.double_little_endian().Write(value); writer.double_big_endian().Write(value); EXPECT_EQ(doubles, buffer); } ::std::array TestDoubleValue(double value, ::std::uint64_t bits) { const auto doubles = MakeDoubles(bits); auto view = MakeDoublesView(&doubles); EXPECT_EQ(value, view.double_little_endian().Read()); EXPECT_EQ(value, view.double_big_endian().Read()); TestDoubleWrite(value, bits); return doubles; } TEST(Floats, One) { TestFloatValue(+1.0f, 0x3f800000); } TEST(Floats, Fraction) { TestFloatValue(-0.375f, 0xbec00000); } TEST(Floats, MinimumDenorm) { TestFloatValue(::std::exp2(-149.0f), 0x00000001); } TEST(Floats, PlusZero) { auto floats = TestFloatValue(+0.0f, 0x00000000); auto view = MakeFloatsView(&floats); EXPECT_FALSE(::std::signbit(view.float_little_endian().Read())); EXPECT_FALSE(::std::signbit(view.float_big_endian().Read())); } TEST(Floats, MinusZero) { auto floats = TestFloatValue(-0.0f, 0x80000000); auto view = MakeFloatsView(&floats); EXPECT_TRUE(::std::signbit(view.float_little_endian().Read())); EXPECT_TRUE(::std::signbit(view.float_big_endian().Read())); } TEST(Floats, PlusInfinity) { auto floats = MakeFloats(0x7f800000); auto view = MakeFloatsView(&floats); EXPECT_TRUE(::std::isinf(view.float_little_endian().Read())); EXPECT_TRUE(::std::isinf(view.float_big_endian().Read())); EXPECT_FALSE(::std::signbit(view.float_little_endian().Read())); EXPECT_FALSE(::std::signbit(view.float_big_endian().Read())); TestFloatWrite(view.float_little_endian().Read(), 0x7f800000); } TEST(Floats, MinusInfinity) { auto floats = MakeFloats(0xff800000); auto view = MakeFloatsView(&floats); EXPECT_TRUE(::std::isinf(view.float_little_endian().Read())); EXPECT_TRUE(::std::isinf(view.float_big_endian().Read())); EXPECT_TRUE(::std::signbit(view.float_little_endian().Read())); EXPECT_TRUE(::std::signbit(view.float_big_endian().Read())); TestFloatWrite(view.float_little_endian().Read(), 0xff800000); } TEST(Floats, Nan) { // TODO(bolms): IEEE 754 does not specify the difference between quiet and // signalling NaN, and there are two completely incompatible definitions in // use by modern processors. Ideally, Emboss should provide some way to // specify which convention is in use, but in practice it probably doesn't // matter when dealing with hardware devices. // // Note that the above bit patterns are signalling NaNs on some processors, // and thus any operation on them other than 'std::isnan' should be avoided. auto floats = MakeFloats(0x7f800001); auto view = MakeFloatsView(&floats); EXPECT_TRUE(::std::isnan(view.float_little_endian().Read())); EXPECT_TRUE(::std::isnan(view.float_big_endian().Read())); TestFloatWrite(view.float_little_endian().Read(), 0x7f800001); } TEST(FloatView, Equals) { auto buf_x = MakeFloats(64); auto buf_y = MakeFloats(64); EXPECT_EQ(buf_x, buf_y); auto x = MakeFloatsView(&buf_x); auto x_const = MakeFloatsView(static_cast*>(&buf_x)); auto y = MakeFloatsView(&buf_y); EXPECT_TRUE(x.Equals(x)); EXPECT_TRUE(x.UncheckedEquals(x)); EXPECT_TRUE(y.Equals(y)); EXPECT_TRUE(y.UncheckedEquals(y)); EXPECT_TRUE(x.Equals(y)); EXPECT_TRUE(x.UncheckedEquals(y)); EXPECT_TRUE(y.Equals(x)); EXPECT_TRUE(y.UncheckedEquals(x)); EXPECT_TRUE(x_const.Equals(y)); EXPECT_TRUE(x_const.UncheckedEquals(y)); EXPECT_TRUE(y.Equals(x_const)); EXPECT_TRUE(y.UncheckedEquals(x_const)); ++buf_y[1]; EXPECT_FALSE(x.Equals(y)); EXPECT_FALSE(x.UncheckedEquals(y)); EXPECT_FALSE(y.Equals(x)); EXPECT_FALSE(y.UncheckedEquals(x)); } TEST(FloatView, EqualsNaN) { auto buf_x = MakeFloats(0x7f800001); auto buf_y = MakeFloats(0x7f800001); EXPECT_EQ(buf_x, buf_y); auto x = MakeFloatsView(&buf_x); auto y = MakeFloatsView(&buf_y); EXPECT_TRUE(::std::isnan(x.float_little_endian().Read())); EXPECT_TRUE(::std::isnan(x.float_big_endian().Read())); EXPECT_TRUE(::std::isnan(y.float_little_endian().Read())); EXPECT_TRUE(::std::isnan(y.float_big_endian().Read())); EXPECT_FALSE(x.Equals(x)); EXPECT_FALSE(y.Equals(y)); EXPECT_FALSE(x.Equals(y)); EXPECT_FALSE(y.Equals(x)); } TEST(Doubles, One) { TestDoubleValue(+1.0, 0x3ff0000000000000UL); } TEST(Doubles, Fraction) { TestDoubleValue(-0.375, 0xbfd8000000000000UL); } TEST(Doubles, MinimumDenorm) { TestDoubleValue(::std::exp2(-1074.0), 0x0000000000000001UL); } TEST(Doubles, PlusZero) { auto doubles = TestDoubleValue(+0.0, 0x0000000000000000UL); auto view = MakeDoublesView(&doubles); EXPECT_FALSE(::std::signbit(view.double_little_endian().Read())); EXPECT_FALSE(::std::signbit(view.double_big_endian().Read())); } TEST(Doubles, MinusZero) { auto doubles = TestDoubleValue(-0.0, 0x8000000000000000UL); auto view = MakeDoublesView(&doubles); EXPECT_TRUE(::std::signbit(view.double_little_endian().Read())); EXPECT_TRUE(::std::signbit(view.double_big_endian().Read())); } TEST(Doubles, PlusInfinity) { auto doubles = MakeDoubles(0x7ff0000000000000UL); auto view = MakeDoublesView(&doubles); EXPECT_TRUE(::std::isinf(view.double_little_endian().Read())); EXPECT_TRUE(::std::isinf(view.double_big_endian().Read())); EXPECT_FALSE(::std::signbit(view.double_little_endian().Read())); EXPECT_FALSE(::std::signbit(view.double_big_endian().Read())); TestDoubleWrite(view.double_little_endian().Read(), 0x7ff0000000000000UL); } TEST(Doubles, MinusInfinity) { auto doubles = MakeDoubles(0xfff0000000000000UL); auto view = MakeDoublesView(&doubles); EXPECT_TRUE(::std::isinf(view.double_little_endian().Read())); EXPECT_TRUE(::std::isinf(view.double_big_endian().Read())); EXPECT_TRUE(::std::signbit(view.double_little_endian().Read())); EXPECT_TRUE(::std::signbit(view.double_big_endian().Read())); TestDoubleWrite(view.double_little_endian().Read(), 0xfff0000000000000UL); } TEST(Doubles, Nan) { auto doubles = MakeDoubles(0x7ff0000000000001UL); auto view = MakeDoublesView(&doubles); EXPECT_TRUE(::std::isnan(view.double_little_endian().Read())); EXPECT_TRUE(::std::isnan(view.double_big_endian().Read())); TestDoubleWrite(view.double_little_endian().Read(), 0x7ff0000000000001UL); } TEST(Doubles, CopyFrom) { auto doubles_x = MakeDoubles(0x7ff0000000000001UL); auto doubles_y = MakeDoubles(0x0000000000000000UL); auto x = MakeDoublesView(&doubles_x); auto y = MakeDoublesView(&doubles_y); EXPECT_NE(x.double_little_endian().Read(), y.double_little_endian().Read()); EXPECT_NE(x.double_big_endian().Read(), y.double_big_endian().Read()); x.double_little_endian().CopyFrom(y.double_little_endian()); x.double_big_endian().CopyFrom(y.double_big_endian()); EXPECT_EQ(x.double_little_endian().Read(), y.double_little_endian().Read()); EXPECT_EQ(x.double_big_endian().Read(), y.double_big_endian().Read()); } TEST(Doubles, TryToCopyFrom) { auto doubles_x = MakeDoubles(0x7ff0000000000001UL); auto doubles_y = MakeDoubles(0x0000000000000000UL); auto x = MakeDoublesView(&doubles_x); auto y = MakeDoublesView(&doubles_y); EXPECT_NE(x.double_little_endian().Read(), y.double_little_endian().Read()); EXPECT_NE(x.double_big_endian().Read(), y.double_big_endian().Read()); EXPECT_TRUE(x.double_little_endian().TryToCopyFrom(y.double_little_endian())); EXPECT_TRUE(x.double_big_endian().TryToCopyFrom(y.double_big_endian())); EXPECT_EQ(x.double_little_endian().Read(), y.double_little_endian().Read()); EXPECT_EQ(x.double_big_endian().Read(), y.double_big_endian().Read()); } TEST(DoubleView, Equals) { auto buf_x = MakeDoubles(64); auto buf_y = MakeDoubles(64); EXPECT_EQ(buf_x, buf_y); auto x = MakeDoublesView(&buf_x); auto y = MakeDoublesView(&buf_y); EXPECT_TRUE(x.Equals(x)); EXPECT_TRUE(y.Equals(y)); EXPECT_TRUE(x.Equals(y)); EXPECT_TRUE(y.Equals(x)); ++buf_y[1]; EXPECT_FALSE(x.Equals(y)); EXPECT_FALSE(y.Equals(x)); } TEST(DoubleView, EqualsNaN) { auto buf_x = MakeDoubles(0x7ff0000000000001UL); auto buf_y = MakeDoubles(0x7ff0000000000001UL); EXPECT_EQ(buf_x, buf_y); auto x = MakeDoublesView(&buf_x); auto y = MakeDoublesView(&buf_y); EXPECT_TRUE(::std::isnan(x.double_little_endian().Read())); EXPECT_TRUE(::std::isnan(x.double_big_endian().Read())); EXPECT_TRUE(::std::isnan(y.double_little_endian().Read())); EXPECT_TRUE(::std::isnan(y.double_big_endian().Read())); EXPECT_FALSE(x.Equals(x)); EXPECT_FALSE(y.Equals(y)); EXPECT_FALSE(x.Equals(y)); EXPECT_FALSE(y.Equals(x)); } TEST(DoubleView, WriteTextFormat) { auto buf_x = MakeDoubles(0x4050000000000000UL); auto x = MakeDoublesView(&buf_x); EXPECT_EQ("{ double_little_endian: 64, double_big_endian: 64 }", ::emboss::WriteToString(x)); EXPECT_EQ( "{\n" " double_little_endian: 64\n" " double_big_endian: 64\n" "}", ::emboss::WriteToString(x, ::emboss::MultilineText())); } TEST(DoubleView, ReadTextFormat) { auto buf_x = MakeDoubles(0UL); auto x = MakeDoublesView(&buf_x); EXPECT_TRUE(::emboss::UpdateFromText(x, "{\n" " double_little_endian: 64\n" " double_big_endian: 64\n" "}")); EXPECT_EQ(64, x.double_little_endian().Read()); EXPECT_EQ(64, x.double_big_endian().Read()); } } // namespace } // namespace test } // namespace emboss