| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625 |
- // Copyright (c) 2015-2016 The Khronos Group Inc.
- //
- // 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
- //
- // http://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.
- #include <cfloat>
- #include <cmath>
- #include <cstdio>
- #include <limits>
- #include <sstream>
- #include <string>
- #include <tuple>
- #include <utility>
- #include <vector>
- #include "gmock/gmock.h"
- #include "source/util/hex_float.h"
- #include "test/unit_spirv.h"
- namespace spvtools {
- namespace utils {
- namespace {
- using ::testing::Eq;
- // In this file "encode" means converting a number into a string,
- // and "decode" means converting a string into a number.
- using HexFloatTest =
- ::testing::TestWithParam<std::pair<FloatProxy<float>, std::string>>;
- using DecodeHexFloatTest =
- ::testing::TestWithParam<std::pair<std::string, FloatProxy<float>>>;
- using HexDoubleTest =
- ::testing::TestWithParam<std::pair<FloatProxy<double>, std::string>>;
- using DecodeHexDoubleTest =
- ::testing::TestWithParam<std::pair<std::string, FloatProxy<double>>>;
- using RoundTripFloatTest = ::testing::TestWithParam<float>;
- using RoundTripDoubleTest = ::testing::TestWithParam<double>;
- // Hex-encodes a float value.
- template <typename T>
- std::string EncodeViaHexFloat(const T& value) {
- std::stringstream ss;
- ss << HexFloat<T>(value);
- return ss.str();
- }
- // The following two tests can't be DRY because they take different parameter
- // types.
- TEST_P(HexFloatTest, EncodeCorrectly) {
- EXPECT_THAT(EncodeViaHexFloat(GetParam().first), Eq(GetParam().second));
- }
- TEST_P(HexDoubleTest, EncodeCorrectly) {
- EXPECT_THAT(EncodeViaHexFloat(GetParam().first), Eq(GetParam().second));
- }
- // Decodes a hex-float string.
- template <typename T>
- FloatProxy<T> Decode(const std::string& str) {
- HexFloat<FloatProxy<T>> decoded(0.f);
- EXPECT_TRUE((std::stringstream(str) >> decoded).eof());
- return decoded.value();
- }
- TEST_P(HexFloatTest, DecodeCorrectly) {
- EXPECT_THAT(Decode<float>(GetParam().second), Eq(GetParam().first));
- }
- TEST_P(HexDoubleTest, DecodeCorrectly) {
- EXPECT_THAT(Decode<double>(GetParam().second), Eq(GetParam().first));
- }
- INSTANTIATE_TEST_SUITE_P(
- Float32Tests, HexFloatTest,
- ::testing::ValuesIn(std::vector<std::pair<FloatProxy<float>, std::string>>({
- {0.f, "0x0p+0"},
- {1.f, "0x1p+0"},
- {2.f, "0x1p+1"},
- {3.f, "0x1.8p+1"},
- {0.5f, "0x1p-1"},
- {0.25f, "0x1p-2"},
- {0.75f, "0x1.8p-1"},
- {-0.f, "-0x0p+0"},
- {-1.f, "-0x1p+0"},
- {-0.5f, "-0x1p-1"},
- {-0.25f, "-0x1p-2"},
- {-0.75f, "-0x1.8p-1"},
- // Larger numbers
- {512.f, "0x1p+9"},
- {-512.f, "-0x1p+9"},
- {1024.f, "0x1p+10"},
- {-1024.f, "-0x1p+10"},
- {1024.f + 8.f, "0x1.02p+10"},
- {-1024.f - 8.f, "-0x1.02p+10"},
- // Small numbers
- {1.0f / 512.f, "0x1p-9"},
- {1.0f / -512.f, "-0x1p-9"},
- {1.0f / 1024.f, "0x1p-10"},
- {1.0f / -1024.f, "-0x1p-10"},
- {1.0f / 1024.f + 1.0f / 8.f, "0x1.02p-3"},
- {1.0f / -1024.f - 1.0f / 8.f, "-0x1.02p-3"},
- // lowest non-denorm
- {float(ldexp(1.0f, -126)), "0x1p-126"},
- {float(ldexp(-1.0f, -126)), "-0x1p-126"},
- // Denormalized values
- {float(ldexp(1.0f, -127)), "0x1p-127"},
- {float(ldexp(1.0f, -127) / 2.0f), "0x1p-128"},
- {float(ldexp(1.0f, -127) / 4.0f), "0x1p-129"},
- {float(ldexp(1.0f, -127) / 8.0f), "0x1p-130"},
- {float(ldexp(-1.0f, -127)), "-0x1p-127"},
- {float(ldexp(-1.0f, -127) / 2.0f), "-0x1p-128"},
- {float(ldexp(-1.0f, -127) / 4.0f), "-0x1p-129"},
- {float(ldexp(-1.0f, -127) / 8.0f), "-0x1p-130"},
- {float(ldexp(1.0, -127) + (ldexp(1.0, -127) / 2.0f)), "0x1.8p-127"},
- {float(ldexp(1.0, -127) / 2.0 + (ldexp(1.0, -127) / 4.0f)),
- "0x1.8p-128"},
- })));
- INSTANTIATE_TEST_SUITE_P(
- Float32NanTests, HexFloatTest,
- ::testing::ValuesIn(std::vector<std::pair<FloatProxy<float>, std::string>>({
- // Various NAN and INF cases
- {uint32_t(0xFF800000), "-0x1p+128"}, // -inf
- {uint32_t(0x7F800000), "0x1p+128"}, // inf
- {uint32_t(0xFFC00000), "-0x1.8p+128"}, // -nan
- {uint32_t(0xFF800100), "-0x1.0002p+128"}, // -nan
- {uint32_t(0xFF800c00), "-0x1.0018p+128"}, // -nan
- {uint32_t(0xFF80F000), "-0x1.01ep+128"}, // -nan
- {uint32_t(0xFFFFFFFF), "-0x1.fffffep+128"}, // -nan
- {uint32_t(0x7FC00000), "0x1.8p+128"}, // +nan
- {uint32_t(0x7F800100), "0x1.0002p+128"}, // +nan
- {uint32_t(0x7f800c00), "0x1.0018p+128"}, // +nan
- {uint32_t(0x7F80F000), "0x1.01ep+128"}, // +nan
- {uint32_t(0x7FFFFFFF), "0x1.fffffep+128"}, // +nan
- })));
- INSTANTIATE_TEST_SUITE_P(
- Float64Tests, HexDoubleTest,
- ::testing::ValuesIn(
- std::vector<std::pair<FloatProxy<double>, std::string>>({
- {0., "0x0p+0"},
- {1., "0x1p+0"},
- {2., "0x1p+1"},
- {3., "0x1.8p+1"},
- {0.5, "0x1p-1"},
- {0.25, "0x1p-2"},
- {0.75, "0x1.8p-1"},
- {-0., "-0x0p+0"},
- {-1., "-0x1p+0"},
- {-0.5, "-0x1p-1"},
- {-0.25, "-0x1p-2"},
- {-0.75, "-0x1.8p-1"},
- // Larger numbers
- {512., "0x1p+9"},
- {-512., "-0x1p+9"},
- {1024., "0x1p+10"},
- {-1024., "-0x1p+10"},
- {1024. + 8., "0x1.02p+10"},
- {-1024. - 8., "-0x1.02p+10"},
- // Large outside the range of normal floats
- {ldexp(1.0, 128), "0x1p+128"},
- {ldexp(1.0, 129), "0x1p+129"},
- {ldexp(-1.0, 128), "-0x1p+128"},
- {ldexp(-1.0, 129), "-0x1p+129"},
- {ldexp(1.0, 128) + ldexp(1.0, 90), "0x1.0000000004p+128"},
- {ldexp(1.0, 129) + ldexp(1.0, 120), "0x1.008p+129"},
- {ldexp(-1.0, 128) + ldexp(1.0, 90), "-0x1.fffffffff8p+127"},
- {ldexp(-1.0, 129) + ldexp(1.0, 120), "-0x1.ffp+128"},
- // Small numbers
- {1.0 / 512., "0x1p-9"},
- {1.0 / -512., "-0x1p-9"},
- {1.0 / 1024., "0x1p-10"},
- {1.0 / -1024., "-0x1p-10"},
- {1.0 / 1024. + 1.0 / 8., "0x1.02p-3"},
- {1.0 / -1024. - 1.0 / 8., "-0x1.02p-3"},
- // Small outside the range of normal floats
- {ldexp(1.0, -128), "0x1p-128"},
- {ldexp(1.0, -129), "0x1p-129"},
- {ldexp(-1.0, -128), "-0x1p-128"},
- {ldexp(-1.0, -129), "-0x1p-129"},
- {ldexp(1.0, -128) + ldexp(1.0, -90), "0x1.0000000004p-90"},
- {ldexp(1.0, -129) + ldexp(1.0, -120), "0x1.008p-120"},
- {ldexp(-1.0, -128) + ldexp(1.0, -90), "0x1.fffffffff8p-91"},
- {ldexp(-1.0, -129) + ldexp(1.0, -120), "0x1.ffp-121"},
- // lowest non-denorm
- {ldexp(1.0, -1022), "0x1p-1022"},
- {ldexp(-1.0, -1022), "-0x1p-1022"},
- // Denormalized values
- {ldexp(1.0, -1023), "0x1p-1023"},
- {ldexp(1.0, -1023) / 2.0, "0x1p-1024"},
- {ldexp(1.0, -1023) / 4.0, "0x1p-1025"},
- {ldexp(1.0, -1023) / 8.0, "0x1p-1026"},
- {ldexp(-1.0, -1024), "-0x1p-1024"},
- {ldexp(-1.0, -1024) / 2.0, "-0x1p-1025"},
- {ldexp(-1.0, -1024) / 4.0, "-0x1p-1026"},
- {ldexp(-1.0, -1024) / 8.0, "-0x1p-1027"},
- {ldexp(1.0, -1023) + (ldexp(1.0, -1023) / 2.0), "0x1.8p-1023"},
- {ldexp(1.0, -1023) / 2.0 + (ldexp(1.0, -1023) / 4.0),
- "0x1.8p-1024"},
- })));
- INSTANTIATE_TEST_SUITE_P(
- Float64NanTests, HexDoubleTest,
- ::testing::ValuesIn(std::vector<
- std::pair<FloatProxy<double>, std::string>>({
- // Various NAN and INF cases
- {uint64_t(0xFFF0000000000000LL), "-0x1p+1024"}, // -inf
- {uint64_t(0x7FF0000000000000LL), "0x1p+1024"}, // +inf
- {uint64_t(0xFFF8000000000000LL), "-0x1.8p+1024"}, // -nan
- {uint64_t(0xFFF0F00000000000LL), "-0x1.0fp+1024"}, // -nan
- {uint64_t(0xFFF0000000000001LL), "-0x1.0000000000001p+1024"}, // -nan
- {uint64_t(0xFFF0000300000000LL), "-0x1.00003p+1024"}, // -nan
- {uint64_t(0xFFFFFFFFFFFFFFFFLL), "-0x1.fffffffffffffp+1024"}, // -nan
- {uint64_t(0x7FF8000000000000LL), "0x1.8p+1024"}, // +nan
- {uint64_t(0x7FF0F00000000000LL), "0x1.0fp+1024"}, // +nan
- {uint64_t(0x7FF0000000000001LL), "0x1.0000000000001p+1024"}, // -nan
- {uint64_t(0x7FF0000300000000LL), "0x1.00003p+1024"}, // -nan
- {uint64_t(0x7FFFFFFFFFFFFFFFLL), "0x1.fffffffffffffp+1024"}, // -nan
- })));
- // Tests that encoding a value and decoding it again restores
- // the same value.
- TEST_P(RoundTripFloatTest, CanStoreAccurately) {
- std::stringstream ss;
- ss << FloatProxy<float>(GetParam());
- ss.seekg(0);
- FloatProxy<float> res;
- ss >> res;
- EXPECT_THAT(GetParam(), Eq(res.getAsFloat()));
- }
- TEST_P(RoundTripDoubleTest, CanStoreAccurately) {
- std::stringstream ss;
- ss << FloatProxy<double>(GetParam());
- ss.seekg(0);
- FloatProxy<double> res;
- ss >> res;
- EXPECT_THAT(GetParam(), Eq(res.getAsFloat()));
- }
- INSTANTIATE_TEST_SUITE_P(
- Float32StoreTests, RoundTripFloatTest,
- ::testing::ValuesIn(std::vector<float>(
- {// Value requiring more than 6 digits of precision to be
- // represented accurately.
- 3.0000002f})));
- INSTANTIATE_TEST_SUITE_P(
- Float64StoreTests, RoundTripDoubleTest,
- ::testing::ValuesIn(std::vector<double>(
- {// Value requiring more than 15 digits of precision to be
- // represented accurately.
- 1.5000000000000002})));
- TEST(HexFloatStreamTest, OperatorLeftShiftPreservesFloatAndFill) {
- std::stringstream s;
- s << std::setw(4) << std::oct << std::setfill('x') << 8 << " "
- << FloatProxy<float>(uint32_t(0xFF800100)) << " " << std::setw(4) << 9;
- EXPECT_THAT(s.str(), Eq(std::string("xx10 -0x1.0002p+128 xx11")));
- }
- TEST(HexDoubleStreamTest, OperatorLeftShiftPreservesFloatAndFill) {
- std::stringstream s;
- s << std::setw(4) << std::oct << std::setfill('x') << 8 << " "
- << FloatProxy<double>(uint64_t(0x7FF0F00000000000LL)) << " " << std::setw(4)
- << 9;
- EXPECT_THAT(s.str(), Eq(std::string("xx10 0x1.0fp+1024 xx11")));
- }
- TEST_P(DecodeHexFloatTest, DecodeCorrectly) {
- EXPECT_THAT(Decode<float>(GetParam().first), Eq(GetParam().second));
- }
- TEST_P(DecodeHexDoubleTest, DecodeCorrectly) {
- EXPECT_THAT(Decode<double>(GetParam().first), Eq(GetParam().second));
- }
- INSTANTIATE_TEST_SUITE_P(
- Float32DecodeTests, DecodeHexFloatTest,
- ::testing::ValuesIn(std::vector<std::pair<std::string, FloatProxy<float>>>({
- {"0x0p+000", 0.f},
- {"0x0p0", 0.f},
- {"0x0p-0", 0.f},
- // flush to zero cases
- {"0x1p-500", 0.f}, // Exponent underflows.
- {"-0x1p-500", -0.f},
- {"0x0.00000000001p-126", 0.f}, // Fraction causes underflow.
- {"-0x0.0000000001p-127", -0.f},
- {"-0x0.01p-142", -0.f}, // Fraction causes additional underflow.
- {"0x0.01p-142", 0.f},
- // Some floats that do not encode the same way as they decode.
- {"0x2p+0", 2.f},
- {"0xFFp+0", 255.f},
- {"0x0.8p+0", 0.5f},
- {"0x0.4p+0", 0.25f},
- })));
- INSTANTIATE_TEST_SUITE_P(
- Float32DecodeInfTests, DecodeHexFloatTest,
- ::testing::ValuesIn(std::vector<std::pair<std::string, FloatProxy<float>>>({
- // inf cases
- {"-0x1p+128", uint32_t(0xFF800000)}, // -inf
- {"0x32p+127", uint32_t(0x7F800000)}, // inf
- {"0x32p+500", uint32_t(0x7F800000)}, // inf
- {"-0x32p+127", uint32_t(0xFF800000)}, // -inf
- })));
- INSTANTIATE_TEST_SUITE_P(
- Float64DecodeTests, DecodeHexDoubleTest,
- ::testing::ValuesIn(
- std::vector<std::pair<std::string, FloatProxy<double>>>({
- {"0x0p+000", 0.},
- {"0x0p0", 0.},
- {"0x0p-0", 0.},
- // flush to zero cases
- {"0x1p-5000", 0.}, // Exponent underflows.
- {"-0x1p-5000", -0.},
- {"0x0.0000000000000001p-1023", 0.}, // Fraction causes underflow.
- {"-0x0.000000000000001p-1024", -0.},
- {"-0x0.01p-1090", -0.f}, // Fraction causes additional underflow.
- {"0x0.01p-1090", 0.},
- // Some floats that do not encode the same way as they decode.
- {"0x2p+0", 2.},
- {"0xFFp+0", 255.},
- {"0x0.8p+0", 0.5},
- {"0x0.4p+0", 0.25},
- })));
- INSTANTIATE_TEST_SUITE_P(
- Float64DecodeInfTests, DecodeHexDoubleTest,
- ::testing::ValuesIn(
- std::vector<std::pair<std::string, FloatProxy<double>>>({
- // inf cases
- {"-0x1p+1024", uint64_t(0xFFF0000000000000)}, // -inf
- {"0x32p+1023", uint64_t(0x7FF0000000000000)}, // inf
- {"0x32p+5000", uint64_t(0x7FF0000000000000)}, // inf
- {"-0x32p+1023", uint64_t(0xFFF0000000000000)}, // -inf
- })));
- TEST(FloatProxy, ValidConversion) {
- EXPECT_THAT(FloatProxy<float>(1.f).getAsFloat(), Eq(1.0f));
- EXPECT_THAT(FloatProxy<float>(32.f).getAsFloat(), Eq(32.0f));
- EXPECT_THAT(FloatProxy<float>(-1.f).getAsFloat(), Eq(-1.0f));
- EXPECT_THAT(FloatProxy<float>(0.f).getAsFloat(), Eq(0.0f));
- EXPECT_THAT(FloatProxy<float>(-0.f).getAsFloat(), Eq(-0.0f));
- EXPECT_THAT(FloatProxy<float>(1.2e32f).getAsFloat(), Eq(1.2e32f));
- EXPECT_TRUE(std::isinf(FloatProxy<float>(uint32_t(0xFF800000)).getAsFloat()));
- EXPECT_TRUE(std::isinf(FloatProxy<float>(uint32_t(0x7F800000)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0xFFC00000)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0xFF800100)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0xFF800c00)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0xFF80F000)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0xFFFFFFFF)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0x7FC00000)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0x7F800100)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0x7f800c00)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0x7F80F000)).getAsFloat()));
- EXPECT_TRUE(std::isnan(FloatProxy<float>(uint32_t(0x7FFFFFFF)).getAsFloat()));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0xFF800000)).data(), Eq(0xFF800000u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0x7F800000)).data(), Eq(0x7F800000u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0xFFC00000)).data(), Eq(0xFFC00000u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0xFF800100)).data(), Eq(0xFF800100u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0xFF800c00)).data(), Eq(0xFF800c00u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0xFF80F000)).data(), Eq(0xFF80F000u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0xFFFFFFFF)).data(), Eq(0xFFFFFFFFu));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0x7FC00000)).data(), Eq(0x7FC00000u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0x7F800100)).data(), Eq(0x7F800100u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0x7f800c00)).data(), Eq(0x7f800c00u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0x7F80F000)).data(), Eq(0x7F80F000u));
- EXPECT_THAT(FloatProxy<float>(uint32_t(0x7FFFFFFF)).data(), Eq(0x7FFFFFFFu));
- }
- TEST(FloatProxy, Nan) {
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0xFFC00000)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0xFF800100)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0xFF800c00)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0xFF80F000)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0xFFFFFFFF)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0x7FC00000)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0x7F800100)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0x7f800c00)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0x7F80F000)).isNan());
- EXPECT_TRUE(FloatProxy<float>(uint32_t(0x7FFFFFFF)).isNan());
- }
- TEST(FloatProxy, Negation) {
- EXPECT_THAT((-FloatProxy<float>(1.f)).getAsFloat(), Eq(-1.0f));
- EXPECT_THAT((-FloatProxy<float>(0.f)).getAsFloat(), Eq(-0.0f));
- EXPECT_THAT((-FloatProxy<float>(-1.f)).getAsFloat(), Eq(1.0f));
- EXPECT_THAT((-FloatProxy<float>(-0.f)).getAsFloat(), Eq(0.0f));
- EXPECT_THAT((-FloatProxy<float>(32.f)).getAsFloat(), Eq(-32.0f));
- EXPECT_THAT((-FloatProxy<float>(-32.f)).getAsFloat(), Eq(32.0f));
- EXPECT_THAT((-FloatProxy<float>(1.2e32f)).getAsFloat(), Eq(-1.2e32f));
- EXPECT_THAT((-FloatProxy<float>(-1.2e32f)).getAsFloat(), Eq(1.2e32f));
- EXPECT_THAT(
- (-FloatProxy<float>(std::numeric_limits<float>::infinity())).getAsFloat(),
- Eq(-std::numeric_limits<float>::infinity()));
- EXPECT_THAT((-FloatProxy<float>(-std::numeric_limits<float>::infinity()))
- .getAsFloat(),
- Eq(std::numeric_limits<float>::infinity()));
- }
- // Test conversion of FloatProxy values to strings.
- //
- // In previous cases, we always wrapped the FloatProxy value in a HexFloat
- // before conversion to a string. In the following cases, the FloatProxy
- // decides for itself whether to print as a regular number or as a hex float.
- using FloatProxyFloatTest =
- ::testing::TestWithParam<std::pair<FloatProxy<float>, std::string>>;
- using FloatProxyDoubleTest =
- ::testing::TestWithParam<std::pair<FloatProxy<double>, std::string>>;
- // Converts a float value to a string via a FloatProxy.
- template <typename T>
- std::string EncodeViaFloatProxy(const T& value) {
- std::stringstream ss;
- ss << value;
- return ss.str();
- }
- // Converts a floating point string so that the exponent prefix
- // is 'e', and the exponent value does not have leading zeros.
- // The Microsoft runtime library likes to write things like "2.5E+010".
- // Convert that to "2.5e+10".
- // We don't care what happens to strings that are not floating point
- // strings.
- std::string NormalizeExponentInFloatString(std::string in) {
- std::string result;
- // Reserve one spot for the terminating null, even when the sscanf fails.
- std::vector<char> prefix(in.size() + 1);
- char e;
- char plus_or_minus;
- int exponent; // in base 10
- if ((4 == std::sscanf(in.c_str(), "%[-+.0123456789]%c%c%d", prefix.data(), &e,
- &plus_or_minus, &exponent)) &&
- (e == 'e' || e == 'E') &&
- (plus_or_minus == '-' || plus_or_minus == '+')) {
- // It looks like a floating point value with exponent.
- std::stringstream out;
- out << prefix.data() << 'e' << plus_or_minus << exponent;
- result = out.str();
- } else {
- result = in;
- }
- return result;
- }
- TEST(NormalizeFloat, Sample) {
- EXPECT_THAT(NormalizeExponentInFloatString(""), Eq(""));
- EXPECT_THAT(NormalizeExponentInFloatString("1e-12"), Eq("1e-12"));
- EXPECT_THAT(NormalizeExponentInFloatString("1E+14"), Eq("1e+14"));
- EXPECT_THAT(NormalizeExponentInFloatString("1e-0012"), Eq("1e-12"));
- EXPECT_THAT(NormalizeExponentInFloatString("1.263E+014"), Eq("1.263e+14"));
- }
- // The following two tests can't be DRY because they take different parameter
- // types.
- TEST_P(FloatProxyFloatTest, EncodeCorrectly) {
- EXPECT_THAT(
- NormalizeExponentInFloatString(EncodeViaFloatProxy(GetParam().first)),
- Eq(GetParam().second));
- }
- TEST_P(FloatProxyDoubleTest, EncodeCorrectly) {
- EXPECT_THAT(
- NormalizeExponentInFloatString(EncodeViaFloatProxy(GetParam().first)),
- Eq(GetParam().second));
- }
- INSTANTIATE_TEST_SUITE_P(
- Float32Tests, FloatProxyFloatTest,
- ::testing::ValuesIn(std::vector<std::pair<FloatProxy<float>, std::string>>({
- // Zero
- {0.f, "0"},
- // Normal numbers
- {1.f, "1"},
- {-0.25f, "-0.25"},
- {1000.0f, "1000"},
- // Still normal numbers, but with large magnitude exponents.
- {float(ldexp(1.f, 126)), "8.50705917e+37"},
- {float(ldexp(-1.f, -126)), "-1.17549435e-38"},
- // denormalized values are printed as hex floats.
- {float(ldexp(1.0f, -127)), "0x1p-127"},
- {float(ldexp(1.5f, -128)), "0x1.8p-128"},
- {float(ldexp(1.25, -129)), "0x1.4p-129"},
- {float(ldexp(1.125, -130)), "0x1.2p-130"},
- {float(ldexp(-1.0f, -127)), "-0x1p-127"},
- {float(ldexp(-1.0f, -128)), "-0x1p-128"},
- {float(ldexp(-1.0f, -129)), "-0x1p-129"},
- {float(ldexp(-1.5f, -130)), "-0x1.8p-130"},
- // NaNs
- {FloatProxy<float>(uint32_t(0xFFC00000)), "-0x1.8p+128"},
- {FloatProxy<float>(uint32_t(0xFF800100)), "-0x1.0002p+128"},
- {std::numeric_limits<float>::infinity(), "0x1p+128"},
- {-std::numeric_limits<float>::infinity(), "-0x1p+128"},
- })));
- INSTANTIATE_TEST_SUITE_P(
- Float64Tests, FloatProxyDoubleTest,
- ::testing::ValuesIn(
- std::vector<std::pair<FloatProxy<double>, std::string>>({
- {0., "0"},
- {1., "1"},
- {-0.25, "-0.25"},
- {1000.0, "1000"},
- // Large outside the range of normal floats
- {ldexp(1.0, 128), "3.4028236692093846e+38"},
- {ldexp(1.5, 129), "1.0208471007628154e+39"},
- {ldexp(-1.0, 128), "-3.4028236692093846e+38"},
- {ldexp(-1.5, 129), "-1.0208471007628154e+39"},
- // Small outside the range of normal floats
- {ldexp(1.5, -129), "2.2040519077917891e-39"},
- {ldexp(-1.5, -129), "-2.2040519077917891e-39"},
- // lowest non-denorm
- {ldexp(1.0, -1022), "2.2250738585072014e-308"},
- {ldexp(-1.0, -1022), "-2.2250738585072014e-308"},
- // Denormalized values
- {ldexp(1.125, -1023), "0x1.2p-1023"},
- {ldexp(-1.375, -1024), "-0x1.6p-1024"},
- // NaNs
- {uint64_t(0x7FF8000000000000LL), "0x1.8p+1024"},
- {uint64_t(0xFFF0F00000000000LL), "-0x1.0fp+1024"},
- // Infinity
- {std::numeric_limits<double>::infinity(), "0x1p+1024"},
- {-std::numeric_limits<double>::infinity(), "-0x1p+1024"},
- })));
- // double is used so that unbiased_exponent can be used with the output
- // of ldexp directly.
- int32_t unbiased_exponent(double f) {
- return HexFloat<FloatProxy<float>>(static_cast<float>(f))
- .getUnbiasedNormalizedExponent();
- }
- int16_t unbiased_half_exponent(uint16_t f) {
- return HexFloat<FloatProxy<Float16>>(f).getUnbiasedNormalizedExponent();
- }
- TEST(HexFloatOperationTest, UnbiasedExponent) {
- // Float cases
- EXPECT_EQ(0, unbiased_exponent(ldexp(1.0f, 0)));
- EXPECT_EQ(-32, unbiased_exponent(ldexp(1.0f, -32)));
- EXPECT_EQ(42, unbiased_exponent(ldexp(1.0f, 42)));
- EXPECT_EQ(125, unbiased_exponent(ldexp(1.0f, 125)));
- EXPECT_EQ(128,
- HexFloat<FloatProxy<float>>(std::numeric_limits<float>::infinity())
- .getUnbiasedNormalizedExponent());
- EXPECT_EQ(-100, unbiased_exponent(ldexp(1.0f, -100)));
- EXPECT_EQ(-127, unbiased_exponent(ldexp(1.0f, -127))); // First denorm
- EXPECT_EQ(-128, unbiased_exponent(ldexp(1.0f, -128)));
- EXPECT_EQ(-129, unbiased_exponent(ldexp(1.0f, -129)));
- EXPECT_EQ(-140, unbiased_exponent(ldexp(1.0f, -140)));
- // Smallest representable number
- EXPECT_EQ(-126 - 23, unbiased_exponent(ldexp(1.0f, -126 - 23)));
- // Should get rounded to 0 first.
- EXPECT_EQ(0, unbiased_exponent(ldexp(1.0f, -127 - 23)));
- // Float16 cases
- // The exponent is represented in the bits 0x7C00
- // The offset is -15
- EXPECT_EQ(0, unbiased_half_exponent(0x3C00));
- EXPECT_EQ(3, unbiased_half_exponent(0x4800));
- EXPECT_EQ(-1, unbiased_half_exponent(0x3800));
- EXPECT_EQ(-14, unbiased_half_exponent(0x0400));
- EXPECT_EQ(16, unbiased_half_exponent(0x7C00));
- EXPECT_EQ(10, unbiased_half_exponent(0x6400));
- // Smallest representable number
- EXPECT_EQ(-24, unbiased_half_exponent(0x0001));
- }
- // Creates a float that is the sum of 1/(2 ^ fractions[i]) for i in factions
- float float_fractions(const std::vector<uint32_t>& fractions) {
- float f = 0;
- for (int32_t i : fractions) {
- f += std::ldexp(1.0f, -i);
- }
- return f;
- }
- // Returns the normalized significand of a HexFloat<FloatProxy<float>>
- // that was created by calling float_fractions with the input fractions,
- // raised to the power of exp.
- uint32_t normalized_significand(const std::vector<uint32_t>& fractions,
- uint32_t exp) {
- return HexFloat<FloatProxy<float>>(
- static_cast<float>(ldexp(float_fractions(fractions), exp)))
- .getNormalizedSignificand();
- }
- // Sets the bits from MSB to LSB of the significand part of a float.
- // For example 0 would set the bit 23 (counting from LSB to MSB),
- // and 1 would set the 22nd bit.
- uint32_t bits_set(const std::vector<uint32_t>& bits) {
- const uint32_t top_bit = 1u << 22u;
- uint32_t val = 0;
- for (uint32_t i : bits) {
- val |= top_bit >> i;
- }
- return val;
- }
- // The same as bits_set but for a Float16 value instead of 32-bit floating
- // point.
- uint16_t half_bits_set(const std::vector<uint32_t>& bits) {
- const uint32_t top_bit = 1u << 9u;
- uint32_t val = 0;
- for (uint32_t i : bits) {
- val |= top_bit >> i;
- }
- return static_cast<uint16_t>(val);
- }
- TEST(HexFloatOperationTest, NormalizedSignificand) {
- // For normalized numbers (the following) it should be a simple matter
- // of getting rid of the top implicit bit
- EXPECT_EQ(bits_set({}), normalized_significand({0}, 0));
- EXPECT_EQ(bits_set({0}), normalized_significand({0, 1}, 0));
- EXPECT_EQ(bits_set({0, 1}), normalized_significand({0, 1, 2}, 0));
- EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 0));
- EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 32));
- EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 126));
- // For denormalized numbers we expect the normalized significand to
- // shift as if it were normalized. This means, in practice that the
- // top_most set bit will be cut off. Looks very similar to above (on purpose)
- EXPECT_EQ(bits_set({}),
- normalized_significand({0}, static_cast<uint32_t>(-127)));
- EXPECT_EQ(bits_set({3}),
- normalized_significand({0, 4}, static_cast<uint32_t>(-128)));
- EXPECT_EQ(bits_set({3}),
- normalized_significand({0, 4}, static_cast<uint32_t>(-127)));
- EXPECT_EQ(bits_set({}),
- normalized_significand({22}, static_cast<uint32_t>(-127)));
- EXPECT_EQ(bits_set({0}),
- normalized_significand({21, 22}, static_cast<uint32_t>(-127)));
- }
- // Returns the 32-bit floating point value created by
- // calling setFromSignUnbiasedExponentAndNormalizedSignificand
- // on a HexFloat<FloatProxy<float>>
- float set_from_sign(bool negative, int32_t unbiased_exponent,
- uint32_t significand, bool round_denorm_up) {
- HexFloat<FloatProxy<float>> f(0.f);
- f.setFromSignUnbiasedExponentAndNormalizedSignificand(
- negative, unbiased_exponent, significand, round_denorm_up);
- return f.value().getAsFloat();
- }
- TEST(HexFloatOperationTests,
- SetFromSignUnbiasedExponentAndNormalizedSignificand) {
- EXPECT_EQ(1.f, set_from_sign(false, 0, 0, false));
- // Tests insertion of various denormalized numbers with and without round up.
- EXPECT_EQ(static_cast<float>(ldexp(1.f, -149)),
- set_from_sign(false, -149, 0, false));
- EXPECT_EQ(static_cast<float>(ldexp(1.f, -149)),
- set_from_sign(false, -149, 0, true));
- EXPECT_EQ(0.f, set_from_sign(false, -150, 1, false));
- EXPECT_EQ(static_cast<float>(ldexp(1.f, -149)),
- set_from_sign(false, -150, 1, true));
- EXPECT_EQ(ldexp(1.0f, -127), set_from_sign(false, -127, 0, false));
- EXPECT_EQ(ldexp(1.0f, -128), set_from_sign(false, -128, 0, false));
- EXPECT_EQ(float_fractions({0, 1, 2, 5}),
- set_from_sign(false, 0, bits_set({0, 1, 4}), false));
- EXPECT_EQ(ldexp(float_fractions({0, 1, 2, 5}), -32),
- set_from_sign(false, -32, bits_set({0, 1, 4}), false));
- EXPECT_EQ(ldexp(float_fractions({0, 1, 2, 5}), -128),
- set_from_sign(false, -128, bits_set({0, 1, 4}), false));
- // The negative cases from above.
- EXPECT_EQ(-1.f, set_from_sign(true, 0, 0, false));
- EXPECT_EQ(-ldexp(1.0, -127), set_from_sign(true, -127, 0, false));
- EXPECT_EQ(-ldexp(1.0, -128), set_from_sign(true, -128, 0, false));
- EXPECT_EQ(-float_fractions({0, 1, 2, 5}),
- set_from_sign(true, 0, bits_set({0, 1, 4}), false));
- EXPECT_EQ(-ldexp(float_fractions({0, 1, 2, 5}), -32),
- set_from_sign(true, -32, bits_set({0, 1, 4}), false));
- EXPECT_EQ(-ldexp(float_fractions({0, 1, 2, 5}), -128),
- set_from_sign(true, -128, bits_set({0, 1, 4}), false));
- }
- TEST(HexFloatOperationTests, NonRounding) {
- // Rounding from 32-bit hex-float to 32-bit hex-float should be trivial,
- // except in the denorm case which is a bit more complex.
- using HF = HexFloat<FloatProxy<float>>;
- bool carry_bit = false;
- round_direction rounding[] = {round_direction::kToZero,
- round_direction::kToNearestEven,
- round_direction::kToPositiveInfinity,
- round_direction::kToNegativeInfinity};
- // Everything fits, so this should be straight-forward
- for (round_direction round : rounding) {
- EXPECT_EQ(bits_set({}),
- HF(0.f).getRoundedNormalizedSignificand<HF>(round, &carry_bit));
- EXPECT_FALSE(carry_bit);
- EXPECT_EQ(bits_set({0}),
- HF(float_fractions({0, 1}))
- .getRoundedNormalizedSignificand<HF>(round, &carry_bit));
- EXPECT_FALSE(carry_bit);
- EXPECT_EQ(bits_set({1, 3}),
- HF(float_fractions({0, 2, 4}))
- .getRoundedNormalizedSignificand<HF>(round, &carry_bit));
- EXPECT_FALSE(carry_bit);
- EXPECT_EQ(
- bits_set({0, 1, 4}),
- HF(static_cast<float>(-ldexp(float_fractions({0, 1, 2, 5}), -128)))
- .getRoundedNormalizedSignificand<HF>(round, &carry_bit));
- EXPECT_FALSE(carry_bit);
- EXPECT_EQ(bits_set({0, 1, 4, 22}),
- HF(static_cast<float>(float_fractions({0, 1, 2, 5, 23})))
- .getRoundedNormalizedSignificand<HF>(round, &carry_bit));
- EXPECT_FALSE(carry_bit);
- }
- }
- using RD = round_direction;
- struct RoundSignificandCase {
- float source_float;
- std::pair<int16_t, bool> expected_results;
- round_direction round;
- };
- using HexFloatRoundTest = ::testing::TestWithParam<RoundSignificandCase>;
- TEST_P(HexFloatRoundTest, RoundDownToFP16) {
- using HF = HexFloat<FloatProxy<float>>;
- using HF16 = HexFloat<FloatProxy<Float16>>;
- HF input_value(GetParam().source_float);
- bool carry_bit = false;
- EXPECT_EQ(GetParam().expected_results.first,
- input_value.getRoundedNormalizedSignificand<HF16>(GetParam().round,
- &carry_bit));
- EXPECT_EQ(carry_bit, GetParam().expected_results.second);
- }
- // clang-format off
- INSTANTIATE_TEST_SUITE_P(F32ToF16, HexFloatRoundTest,
- ::testing::ValuesIn(std::vector<RoundSignificandCase>(
- {
- {float_fractions({0}), std::make_pair(half_bits_set({}), false), RD::kToZero},
- {float_fractions({0}), std::make_pair(half_bits_set({}), false), RD::kToNearestEven},
- {float_fractions({0}), std::make_pair(half_bits_set({}), false), RD::kToPositiveInfinity},
- {float_fractions({0}), std::make_pair(half_bits_set({}), false), RD::kToNegativeInfinity},
- {float_fractions({0, 1}), std::make_pair(half_bits_set({0}), false), RD::kToZero},
- {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), RD::kToZero},
- {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0, 9}), false), RD::kToPositiveInfinity},
- {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity},
- {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), RD::kToNearestEven},
- {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 9}), false), RD::kToZero},
- {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 8}), false), RD::kToPositiveInfinity},
- {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNegativeInfinity},
- {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 8}), false), RD::kToNearestEven},
- {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), RD::kToZero},
- {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), RD::kToPositiveInfinity},
- {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity},
- {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven},
- {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), RD::kToZero},
- {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), RD::kToPositiveInfinity},
- {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNegativeInfinity},
- {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven},
- {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0}), false), RD::kToZero},
- {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0, 9}), false), RD::kToPositiveInfinity},
- {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity},
- {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven},
- // Carries
- {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), false), RD::kToZero},
- {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({}), true), RD::kToPositiveInfinity},
- {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), false), RD::kToNegativeInfinity},
- {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({}), true), RD::kToNearestEven},
- // Cases where original number was denorm. Note: this should have no effect
- // the number is pre-normalized.
- {static_cast<float>(ldexp(float_fractions({0, 1, 11, 13}), -128)), std::make_pair(half_bits_set({0}), false), RD::kToZero},
- {static_cast<float>(ldexp(float_fractions({0, 1, 11, 13}), -129)), std::make_pair(half_bits_set({0, 9}), false), RD::kToPositiveInfinity},
- {static_cast<float>(ldexp(float_fractions({0, 1, 11, 13}), -131)), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity},
- {static_cast<float>(ldexp(float_fractions({0, 1, 11, 13}), -130)), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven},
- })));
- // clang-format on
- struct UpCastSignificandCase {
- uint16_t source_half;
- uint32_t expected_result;
- };
- using HexFloatRoundUpSignificandTest =
- ::testing::TestWithParam<UpCastSignificandCase>;
- TEST_P(HexFloatRoundUpSignificandTest, Widening) {
- using HF = HexFloat<FloatProxy<float>>;
- using HF16 = HexFloat<FloatProxy<Float16>>;
- bool carry_bit = false;
- round_direction rounding[] = {round_direction::kToZero,
- round_direction::kToNearestEven,
- round_direction::kToPositiveInfinity,
- round_direction::kToNegativeInfinity};
- // Everything fits, so everything should just be bit-shifts.
- for (round_direction round : rounding) {
- carry_bit = false;
- HF16 input_value(GetParam().source_half);
- EXPECT_EQ(
- GetParam().expected_result,
- input_value.getRoundedNormalizedSignificand<HF>(round, &carry_bit))
- << std::hex << "0x"
- << input_value.getRoundedNormalizedSignificand<HF>(round, &carry_bit)
- << " 0x" << GetParam().expected_result;
- EXPECT_FALSE(carry_bit);
- }
- }
- INSTANTIATE_TEST_SUITE_P(
- F16toF32, HexFloatRoundUpSignificandTest,
- // 0xFC00 of the source 16-bit hex value cover the sign and the exponent.
- // They are ignored for this test.
- ::testing::ValuesIn(std::vector<UpCastSignificandCase>({
- {0x3F00, 0x600000},
- {0x0F00, 0x600000},
- {0x0F01, 0x602000},
- {0x0FFF, 0x7FE000},
- })));
- struct DownCastTest {
- float source_float;
- uint16_t expected_half;
- std::vector<round_direction> directions;
- };
- std::string get_round_text(round_direction direction) {
- #define CASE(round_direction) \
- case round_direction: \
- return #round_direction
- switch (direction) {
- CASE(round_direction::kToZero);
- CASE(round_direction::kToPositiveInfinity);
- CASE(round_direction::kToNegativeInfinity);
- CASE(round_direction::kToNearestEven);
- }
- #undef CASE
- return "";
- }
- using HexFloatFP32To16Tests = ::testing::TestWithParam<DownCastTest>;
- TEST_P(HexFloatFP32To16Tests, NarrowingCasts) {
- using HF = HexFloat<FloatProxy<float>>;
- using HF16 = HexFloat<FloatProxy<Float16>>;
- HF f(GetParam().source_float);
- for (auto round : GetParam().directions) {
- HF16 half(0);
- f.castTo(half, round);
- EXPECT_EQ(GetParam().expected_half, half.value().getAsFloat().get_value())
- << get_round_text(round) << " " << std::hex
- << BitwiseCast<uint32_t>(GetParam().source_float)
- << " cast to: " << half.value().getAsFloat().get_value();
- }
- }
- const uint16_t positive_infinity = 0x7C00;
- const uint16_t negative_infinity = 0xFC00;
- INSTANTIATE_TEST_SUITE_P(
- F32ToF16, HexFloatFP32To16Tests,
- ::testing::ValuesIn(std::vector<DownCastTest>({
- // Exactly representable as half.
- {0.f,
- 0x0,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {-0.f,
- 0x8000,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {1.0f,
- 0x3C00,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {-1.0f,
- 0xBC00,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {float_fractions({0, 1, 10}),
- 0x3E01,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {-float_fractions({0, 1, 10}),
- 0xBE01,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {static_cast<float>(ldexp(float_fractions({0, 1, 10}), 3)),
- 0x4A01,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {static_cast<float>(-ldexp(float_fractions({0, 1, 10}), 3)),
- 0xCA01,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- // Underflow
- {static_cast<float>(ldexp(1.0f, -25)),
- 0x0,
- {RD::kToZero, RD::kToNegativeInfinity, RD::kToNearestEven}},
- {static_cast<float>(ldexp(1.0f, -25)), 0x1, {RD::kToPositiveInfinity}},
- {static_cast<float>(-ldexp(1.0f, -25)),
- 0x8000,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNearestEven}},
- {static_cast<float>(-ldexp(1.0f, -25)),
- 0x8001,
- {RD::kToNegativeInfinity}},
- {static_cast<float>(ldexp(1.0f, -24)),
- 0x1,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- // Overflow
- {static_cast<float>(ldexp(1.0f, 16)),
- positive_infinity,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {static_cast<float>(ldexp(1.0f, 18)),
- positive_infinity,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {static_cast<float>(ldexp(1.3f, 16)),
- positive_infinity,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {static_cast<float>(-ldexp(1.0f, 16)),
- negative_infinity,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {static_cast<float>(-ldexp(1.0f, 18)),
- negative_infinity,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {static_cast<float>(-ldexp(1.3f, 16)),
- negative_infinity,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- // Transfer of Infinities
- {std::numeric_limits<float>::infinity(),
- positive_infinity,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- {-std::numeric_limits<float>::infinity(),
- negative_infinity,
- {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
- RD::kToNearestEven}},
- // Nans are below because we cannot test for equality.
- })));
- struct UpCastCase {
- uint16_t source_half;
- float expected_float;
- };
- using HexFloatFP16To32Tests = ::testing::TestWithParam<UpCastCase>;
- TEST_P(HexFloatFP16To32Tests, WideningCasts) {
- using HF = HexFloat<FloatProxy<float>>;
- using HF16 = HexFloat<FloatProxy<Float16>>;
- HF16 f(GetParam().source_half);
- round_direction rounding[] = {round_direction::kToZero,
- round_direction::kToNearestEven,
- round_direction::kToPositiveInfinity,
- round_direction::kToNegativeInfinity};
- // Everything fits, so everything should just be bit-shifts.
- for (round_direction round : rounding) {
- HF flt(0.f);
- f.castTo(flt, round);
- EXPECT_EQ(GetParam().expected_float, flt.value().getAsFloat())
- << get_round_text(round) << " " << std::hex
- << BitwiseCast<uint16_t>(GetParam().source_half)
- << " cast to: " << flt.value().getAsFloat();
- }
- }
- INSTANTIATE_TEST_SUITE_P(
- F16ToF32, HexFloatFP16To32Tests,
- ::testing::ValuesIn(std::vector<UpCastCase>({
- {0x0000, 0.f},
- {0x8000, -0.f},
- {0x3C00, 1.0f},
- {0xBC00, -1.0f},
- {0x3F00, float_fractions({0, 1, 2})},
- {0xBF00, -float_fractions({0, 1, 2})},
- {0x3F01, float_fractions({0, 1, 2, 10})},
- {0xBF01, -float_fractions({0, 1, 2, 10})},
- // denorm
- {0x0001, static_cast<float>(ldexp(1.0, -24))},
- {0x0002, static_cast<float>(ldexp(1.0, -23))},
- {0x8001, static_cast<float>(-ldexp(1.0, -24))},
- {0x8011, static_cast<float>(-ldexp(1.0, -20) + -ldexp(1.0, -24))},
- // inf
- {0x7C00, std::numeric_limits<float>::infinity()},
- {0xFC00, -std::numeric_limits<float>::infinity()},
- })));
- TEST(HexFloatOperationTests, NanTests) {
- using HF = HexFloat<FloatProxy<float>>;
- using HF16 = HexFloat<FloatProxy<Float16>>;
- round_direction rounding[] = {round_direction::kToZero,
- round_direction::kToNearestEven,
- round_direction::kToPositiveInfinity,
- round_direction::kToNegativeInfinity};
- // Everything fits, so everything should just be bit-shifts.
- for (round_direction round : rounding) {
- HF16 f16(0);
- HF f(0.f);
- HF(std::numeric_limits<float>::quiet_NaN()).castTo(f16, round);
- EXPECT_TRUE(f16.value().isNan());
- HF(std::numeric_limits<float>::signaling_NaN()).castTo(f16, round);
- EXPECT_TRUE(f16.value().isNan());
- HF16(0x7C01).castTo(f, round);
- EXPECT_TRUE(f.value().isNan());
- HF16(0x7C11).castTo(f, round);
- EXPECT_TRUE(f.value().isNan());
- HF16(0xFC01).castTo(f, round);
- EXPECT_TRUE(f.value().isNan());
- HF16(0x7C10).castTo(f, round);
- EXPECT_TRUE(f.value().isNan());
- HF16(0xFF00).castTo(f, round);
- EXPECT_TRUE(f.value().isNan());
- }
- }
- // A test case for parsing good and bad HexFloat<FloatProxy<T>> literals.
- template <typename T>
- struct FloatParseCase {
- std::string literal;
- bool negate_value;
- bool expect_success;
- HexFloat<FloatProxy<T>> expected_value;
- };
- using ParseNormalFloatTest = ::testing::TestWithParam<FloatParseCase<float>>;
- TEST_P(ParseNormalFloatTest, Samples) {
- std::stringstream input(GetParam().literal);
- HexFloat<FloatProxy<float>> parsed_value(0.0f);
- ParseNormalFloat(input, GetParam().negate_value, parsed_value);
- EXPECT_NE(GetParam().expect_success, input.fail())
- << " literal: " << GetParam().literal
- << " negate: " << GetParam().negate_value;
- if (GetParam().expect_success) {
- EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value()))
- << " literal: " << GetParam().literal
- << " negate: " << GetParam().negate_value;
- }
- }
- // Returns a FloatParseCase with expected failure.
- template <typename T>
- FloatParseCase<T> BadFloatParseCase(std::string literal, bool negate_value,
- T expected_value) {
- HexFloat<FloatProxy<T>> proxy_expected_value(expected_value);
- return FloatParseCase<T>{literal, negate_value, false, proxy_expected_value};
- }
- // Returns a FloatParseCase that should successfully parse to a given value.
- template <typename T>
- FloatParseCase<T> GoodFloatParseCase(std::string literal, bool negate_value,
- T expected_value) {
- HexFloat<FloatProxy<T>> proxy_expected_value(expected_value);
- return FloatParseCase<T>{literal, negate_value, true, proxy_expected_value};
- }
- INSTANTIATE_TEST_SUITE_P(
- FloatParse, ParseNormalFloatTest,
- ::testing::ValuesIn(std::vector<FloatParseCase<float>>{
- // Failing cases due to trivially incorrect syntax.
- BadFloatParseCase("abc", false, 0.0f),
- BadFloatParseCase("abc", true, 0.0f),
- // Valid cases.
- GoodFloatParseCase("0", false, 0.0f),
- GoodFloatParseCase("0.0", false, 0.0f),
- GoodFloatParseCase("-0.0", false, -0.0f),
- GoodFloatParseCase("2.0", false, 2.0f),
- GoodFloatParseCase("-2.0", false, -2.0f),
- GoodFloatParseCase("+2.0", false, 2.0f),
- // Cases with negate_value being true.
- GoodFloatParseCase("0.0", true, -0.0f),
- GoodFloatParseCase("2.0", true, -2.0f),
- // When negate_value is true, we should not accept a
- // leading minus or plus.
- BadFloatParseCase("-0.0", true, 0.0f),
- BadFloatParseCase("-2.0", true, 0.0f),
- BadFloatParseCase("+0.0", true, 0.0f),
- BadFloatParseCase("+2.0", true, 0.0f),
- // Overflow is an error for 32-bit float parsing.
- BadFloatParseCase("1e40", false, FLT_MAX),
- BadFloatParseCase("1e40", true, -FLT_MAX),
- BadFloatParseCase("-1e40", false, -FLT_MAX),
- // We can't have -1e40 and negate_value == true since
- // that represents an original case of "--1e40" which
- // is invalid.
- }));
- using ParseNormalFloat16Test =
- ::testing::TestWithParam<FloatParseCase<Float16>>;
- TEST_P(ParseNormalFloat16Test, Samples) {
- std::stringstream input(GetParam().literal);
- HexFloat<FloatProxy<Float16>> parsed_value(0);
- ParseNormalFloat(input, GetParam().negate_value, parsed_value);
- EXPECT_NE(GetParam().expect_success, input.fail())
- << " literal: " << GetParam().literal
- << " negate: " << GetParam().negate_value;
- if (GetParam().expect_success) {
- EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value()))
- << " literal: " << GetParam().literal
- << " negate: " << GetParam().negate_value;
- }
- }
- INSTANTIATE_TEST_SUITE_P(
- Float16Parse, ParseNormalFloat16Test,
- ::testing::ValuesIn(std::vector<FloatParseCase<Float16>>{
- // Failing cases due to trivially incorrect syntax.
- BadFloatParseCase<Float16>("abc", false, uint16_t{0}),
- BadFloatParseCase<Float16>("abc", true, uint16_t{0}),
- // Valid cases.
- GoodFloatParseCase<Float16>("0", false, uint16_t{0}),
- GoodFloatParseCase<Float16>("0.0", false, uint16_t{0}),
- GoodFloatParseCase<Float16>("-0.0", false, uint16_t{0x8000}),
- GoodFloatParseCase<Float16>("2.0", false, uint16_t{0x4000}),
- GoodFloatParseCase<Float16>("-2.0", false, uint16_t{0xc000}),
- GoodFloatParseCase<Float16>("+2.0", false, uint16_t{0x4000}),
- // Cases with negate_value being true.
- GoodFloatParseCase<Float16>("0.0", true, uint16_t{0x8000}),
- GoodFloatParseCase<Float16>("2.0", true, uint16_t{0xc000}),
- // When negate_value is true, we should not accept a leading minus or
- // plus.
- BadFloatParseCase<Float16>("-0.0", true, uint16_t{0}),
- BadFloatParseCase<Float16>("-2.0", true, uint16_t{0}),
- BadFloatParseCase<Float16>("+0.0", true, uint16_t{0}),
- BadFloatParseCase<Float16>("+2.0", true, uint16_t{0}),
- }));
- // A test case for detecting infinities.
- template <typename T>
- struct OverflowParseCase {
- std::string input;
- bool expect_success;
- T expected_value;
- };
- using FloatProxyParseOverflowFloatTest =
- ::testing::TestWithParam<OverflowParseCase<float>>;
- TEST_P(FloatProxyParseOverflowFloatTest, Sample) {
- std::istringstream input(GetParam().input);
- HexFloat<FloatProxy<float>> value(0.0f);
- input >> value;
- EXPECT_NE(GetParam().expect_success, input.fail());
- if (GetParam().expect_success) {
- EXPECT_THAT(value.value().getAsFloat(), GetParam().expected_value);
- }
- }
- INSTANTIATE_TEST_SUITE_P(
- FloatOverflow, FloatProxyParseOverflowFloatTest,
- ::testing::ValuesIn(std::vector<OverflowParseCase<float>>({
- {"0", true, 0.0f},
- {"0.0", true, 0.0f},
- {"1.0", true, 1.0f},
- {"1e38", true, 1e38f},
- {"-1e38", true, -1e38f},
- {"1e40", false, FLT_MAX},
- {"-1e40", false, -FLT_MAX},
- {"1e400", false, FLT_MAX},
- {"-1e400", false, -FLT_MAX},
- })));
- using FloatProxyParseOverflowDoubleTest =
- ::testing::TestWithParam<OverflowParseCase<double>>;
- TEST_P(FloatProxyParseOverflowDoubleTest, Sample) {
- std::istringstream input(GetParam().input);
- HexFloat<FloatProxy<double>> value(0.0);
- input >> value;
- EXPECT_NE(GetParam().expect_success, input.fail());
- if (GetParam().expect_success) {
- EXPECT_THAT(value.value().getAsFloat(), Eq(GetParam().expected_value));
- }
- }
- INSTANTIATE_TEST_SUITE_P(
- DoubleOverflow, FloatProxyParseOverflowDoubleTest,
- ::testing::ValuesIn(std::vector<OverflowParseCase<double>>({
- {"0", true, 0.0},
- {"0.0", true, 0.0},
- {"1.0", true, 1.0},
- {"1e38", true, 1e38},
- {"-1e38", true, -1e38},
- {"1e40", true, 1e40},
- {"-1e40", true, -1e40},
- {"1e400", false, DBL_MAX},
- {"-1e400", false, -DBL_MAX},
- })));
- using FloatProxyParseOverflowFloat16Test =
- ::testing::TestWithParam<OverflowParseCase<uint16_t>>;
- TEST_P(FloatProxyParseOverflowFloat16Test, Sample) {
- std::istringstream input(GetParam().input);
- HexFloat<FloatProxy<Float16>> value(0);
- input >> value;
- EXPECT_NE(GetParam().expect_success, input.fail())
- << " literal: " << GetParam().input;
- if (GetParam().expect_success) {
- EXPECT_THAT(value.value().data(), Eq(GetParam().expected_value))
- << " literal: " << GetParam().input;
- }
- }
- INSTANTIATE_TEST_SUITE_P(
- Float16Overflow, FloatProxyParseOverflowFloat16Test,
- ::testing::ValuesIn(std::vector<OverflowParseCase<uint16_t>>({
- {"0", true, uint16_t{0}},
- {"0.0", true, uint16_t{0}},
- {"1.0", true, uint16_t{0x3c00}},
- // Overflow for 16-bit float is an error, and returns max or
- // lowest value.
- {"1e38", false, uint16_t{0x7bff}},
- {"1e40", false, uint16_t{0x7bff}},
- {"1e400", false, uint16_t{0x7bff}},
- {"-1e38", false, uint16_t{0xfbff}},
- {"-1e40", false, uint16_t{0xfbff}},
- {"-1e400", false, uint16_t{0xfbff}},
- })));
- TEST(FloatProxy, Max) {
- EXPECT_THAT(FloatProxy<Float16>::max().getAsFloat().get_value(),
- Eq(uint16_t{0x7bff}));
- EXPECT_THAT(FloatProxy<float>::max().getAsFloat(),
- Eq(std::numeric_limits<float>::max()));
- EXPECT_THAT(FloatProxy<double>::max().getAsFloat(),
- Eq(std::numeric_limits<double>::max()));
- }
- TEST(FloatProxy, Lowest) {
- EXPECT_THAT(FloatProxy<Float16>::lowest().getAsFloat().get_value(),
- Eq(uint16_t{0xfbff}));
- EXPECT_THAT(FloatProxy<float>::lowest().getAsFloat(),
- Eq(std::numeric_limits<float>::lowest()));
- EXPECT_THAT(FloatProxy<double>::lowest().getAsFloat(),
- Eq(std::numeric_limits<double>::lowest()));
- }
- template <typename T>
- struct StreamParseCase {
- StreamParseCase(const std::string& lit, bool succ, const std::string& suffix,
- T value)
- : literal(lit),
- expect_success(succ),
- expected_suffix(suffix),
- expected_value(HexFloat<FloatProxy<T>>(value)) {}
- std::string literal;
- bool expect_success;
- std::string expected_suffix;
- HexFloat<FloatProxy<T>> expected_value;
- };
- template <typename T>
- std::ostream& operator<<(std::ostream& os, const StreamParseCase<T>& fspc) {
- os << "StreamParseCase(" << fspc.literal
- << ", expect_success:" << int(fspc.expect_success) << ","
- << fspc.expected_suffix << "," << fspc.expected_value << ")";
- return os;
- }
- using Float32StreamParseTest = ::testing::TestWithParam<StreamParseCase<float>>;
- using Float16StreamParseTest =
- ::testing::TestWithParam<StreamParseCase<Float16>>;
- TEST_P(Float32StreamParseTest, Samples) {
- std::stringstream input(GetParam().literal);
- HexFloat<FloatProxy<float>> parsed_value(0.0f);
- // Hex floats must be read with the stream input operator.
- input >> parsed_value;
- if (GetParam().expect_success) {
- EXPECT_FALSE(input.fail());
- std::string suffix;
- input >> suffix;
- // EXPECT_EQ(suffix, GetParam().expected_suffix);
- EXPECT_EQ(parsed_value.value().getAsFloat(),
- GetParam().expected_value.value().getAsFloat());
- } else {
- EXPECT_TRUE(input.fail());
- }
- }
- // Returns a Float16 constructed from its sign bit, unbiased exponent, and
- // mantissa.
- Float16 makeF16(int sign_bit, int unbiased_exp, int mantissa) {
- EXPECT_LE(0, sign_bit);
- EXPECT_LE(sign_bit, 1);
- // Exponent is 5 bits, with bias of 15.
- EXPECT_LE(-15, unbiased_exp); // -15 means zero or subnormal
- EXPECT_LE(unbiased_exp, 16); // 16 means infinity or NaN
- EXPECT_LE(0, mantissa);
- EXPECT_LE(mantissa, 0x3ff);
- const unsigned biased_exp = 15 + unbiased_exp;
- const uint32_t as_bits = sign_bit << 15 | (biased_exp << 10) | mantissa;
- EXPECT_LE(as_bits, 0xffffu);
- return Float16(static_cast<uint16_t>(as_bits));
- }
- TEST_P(Float16StreamParseTest, Samples) {
- std::stringstream input(GetParam().literal);
- HexFloat<FloatProxy<Float16>> parsed_value(makeF16(0, 0, 0));
- // Hex floats must be read with the stream input operator.
- input >> parsed_value;
- if (GetParam().expect_success) {
- EXPECT_FALSE(input.fail());
- std::string suffix;
- input >> suffix;
- const auto got = parsed_value.value();
- const auto expected = GetParam().expected_value.value();
- EXPECT_EQ(got.data(), expected.data())
- << "got: " << got << " expected: " << expected;
- } else {
- EXPECT_TRUE(input.fail());
- }
- }
- INSTANTIATE_TEST_SUITE_P(
- HexFloat32FillSignificantDigits, Float32StreamParseTest,
- ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
- {"0x123456p0", true, "", ldexpf(0x123456, 0)},
- // Patterns that fill all mantissa bits
- {"0x1.fffffep+23", true, "", ldexpf(0x1fffffe, -1)},
- {"0x1f.ffffep+19", true, "", ldexpf(0x1fffffe, -1)},
- {"0x1ff.fffep+15", true, "", ldexpf(0x1fffffe, -1)},
- {"0x1fff.ffep+11", true, "", ldexpf(0x1fffffe, -1)},
- {"0x1ffff.fep+7", true, "", ldexpf(0x1fffffe, -1)},
- {"0x1fffff.ep+3", true, "", ldexpf(0x1fffffe, -1)},
- {"0x1fffffe.p-1", true, "", ldexpf(0x1fffffe, -1)},
- {"0xffffff.p+0", true, "", ldexpf(0x1fffffe, -1)},
- {"0xffffff.p+0", true, "", ldexpf(0xffffff, 0)},
- // Now drop some bits in the middle
- {"0xa5a5a5.p+0", true, "", ldexpf(0xa5a5a5, 0)},
- {"0x5a5a5a.p+0", true, "", ldexpf(0x5a5a5a, 0)}}));
- INSTANTIATE_TEST_SUITE_P(
- HexFloat32ExcessSignificantDigits, Float32StreamParseTest,
- ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
- // Base cases
- {"0x1.fffffep0", true, "", ldexpf(0xffffff, -23)},
- {"0xa5a5a5p0", true, "", ldexpf(0xa5a5a5, 0)},
- {"0xa.5a5a5p+9", true, "", ldexpf(0xa5a5a5, -11)},
- {"0x5a5a5ap0", true, "", ldexpf(0x5a5a5a, 0)},
- {"0x5.a5a5ap+9", true, "", ldexpf(0x5a5a5a, -11)},
- // Truncate extra bits: zeroes
- {"0x1.fffffe0p0", true, "", ldexpf(0xffffff, -23)},
- {"0xa5a5a5000p0", true, "", ldexpf(0xa5a5a5, 12)},
- {"0xa.5a5a5000p+9", true, "", ldexpf(0xa5a5a5, -11)},
- {"0x5a5a5a000p0", true, "", ldexpf(0x5a5a5a, 12)},
- {"0x5.a5a5a000p+9", true, "", ldexpf(0x5a5a5a, -11)},
- // Truncate extra bits: ones
- {"0x1.ffffffp0", // Extra bits in the last nibble
- true, "", ldexpf(0xffffff, -23)},
- {"0x1.fffffffp0", true, "", ldexpf(0xffffff, -23)},
- {"0xa5a5a5fffp0", true, "", ldexpf(0xa5a5a5, 12)},
- {"0xa.5a5a5fffp+9", true, "", ldexpf(0xa5a5a5, -11)},
- {"0x5a5a5afffp0",
- // The 5 nibble (0101), leads with 0, so the result can fit a leading
- // 1 bit , yielding 8 (1000).
- true, "", ldexpf(0x5a5a5a8, 8)},
- {"0x5.a5a5afffp+9", true, "", ldexpf(0x5a5a5a8, 8 - 32 + 9)}}));
- INSTANTIATE_TEST_SUITE_P(
- HexFloat32ExponentMissingDigits, Float32StreamParseTest,
- ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
- {"0x1.0p1", true, "", 2.0f},
- {"0x1.0p1a", true, "a", 2.0f},
- {"-0x1.0p1f", true, "f", -2.0f},
- {"0x1.0p", false, "", 0.0f},
- {"0x1.0pa", false, "", 0.0f},
- {"0x1.0p!", false, "", 0.0f},
- {"0x1.0p+", false, "", 0.0f},
- {"0x1.0p+a", false, "", 0.0f},
- {"0x1.0p+!", false, "", 0.0f},
- {"0x1.0p-", false, "", 0.0f},
- {"0x1.0p-a", false, "", 0.0f},
- {"0x1.0p-!", false, "", 0.0f},
- {"0x1.0p++", false, "", 0.0f},
- {"0x1.0p+-", false, "", 0.0f},
- {"0x1.0p-+", false, "", 0.0f},
- {"0x1.0p--", false, "", 0.0f}}));
- INSTANTIATE_TEST_SUITE_P(
- HexFloat32ExponentTrailingSign, Float32StreamParseTest,
- ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
- // Don't consume a sign after the binary exponent digits.
- {"0x1.0p1", true, "", 2.0f},
- {"0x1.0p1+", true, "+", 2.0f},
- {"0x1.0p1-", true, "-", 2.0f}}));
- INSTANTIATE_TEST_SUITE_P(
- HexFloat32PositiveExponentOverflow, Float32StreamParseTest,
- ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
- // Positive exponents
- {"0x1.0p1", true, "", 2.0f}, // fine, a normal number
- {"0x1.0p15", true, "", 32768.0f}, // fine, a normal number
- {"0x1.0p127", true, "", float(ldexp(1.0f, 127))}, // good large number
- {"0x0.8p128", true, "", float(ldexp(1.0f, 127))}, // good large number
- {"0x0.1p131", true, "", float(ldexp(1.0f, 127))}, // good large number
- {"0x0.01p135", true, "", float(ldexp(1.0f, 127))}, // good large number
- {"0x1.0p128", true, "", float(ldexp(1.0f, 128))}, // infinity
- {"0x1.0p4294967295", true, "", float(ldexp(1.0f, 128))}, // infinity
- {"0x1.0p5000000000", true, "", float(ldexp(1.0f, 128))}, // infinity
- {"0x0.0p5000000000", true, "", 0.0f}, // zero mantissa, zero result
- }));
- INSTANTIATE_TEST_SUITE_P(
- HexFloat32NegativeExponentOverflow, Float32StreamParseTest,
- ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
- // Positive results, digits before '.'
- {"0x1.0p-126", true, "",
- float(ldexp(1.0f, -126))}, // fine, a small normal number
- {"0x1.0p-127", true, "", float(ldexp(1.0f, -127))}, // denorm number
- {"0x1.0p-149", true, "",
- float(ldexp(1.0f, -149))}, // smallest positive denormal
- {"0x0.8p-148", true, "",
- float(ldexp(1.0f, -149))}, // smallest positive denormal
- {"0x0.1p-145", true, "",
- float(ldexp(1.0f, -149))}, // smallest positive denormal
- {"0x0.01p-141", true, "",
- float(ldexp(1.0f, -149))}, // smallest positive denormal
- // underflow rounds down to zero
- {"0x1.0p-150", true, "", 0.0f},
- {"0x1.0p-4294967296", true, "",
- 0.0f}, // avoid exponent overflow in parser
- {"0x1.0p-5000000000", true, "",
- 0.0f}, // avoid exponent overflow in parser
- {"0x0.0p-5000000000", true, "", 0.0f}, // zero mantissa, zero result
- }));
- INSTANTIATE_TEST_SUITE_P(
- HexFloat16ExcessSignificantDigits, Float16StreamParseTest,
- ::testing::ValuesIn(std::vector<StreamParseCase<Float16>>{
- // Zero
- {"0x1.c00p0", true, "", makeF16(0, 0, 0x300)},
- {"0x0p0", true, "", makeF16(0, -15, 0x0)},
- {"0x000.0000p0", true, "", makeF16(0, -15, 0x0)},
- // All leading 1s
- {"0x1p0", true, "", makeF16(0, 0, 0x0)},
- {"0x1.8p0", true, "", makeF16(0, 0, 0x200)},
- {"0x1.cp0", true, "", makeF16(0, 0, 0x300)},
- {"0x1.ep0", true, "", makeF16(0, 0, 0x380)},
- {"0x1.fp0", true, "", makeF16(0, 0, 0x3c0)},
- {"0x1.f8p0", true, "", makeF16(0, 0, 0x3e0)},
- {"0x1.fcp0", true, "", makeF16(0, 0, 0x3f0)},
- {"0x1.fep0", true, "", makeF16(0, 0, 0x3f8)},
- {"0x1.ffp0", true, "", makeF16(0, 0, 0x3fc)},
- // Fill trailing zeros to all significant places
- // that might be used for significant digits.
- {"0x1.ff8p0", true, "", makeF16(0, 0, 0x3fe)},
- {"0x1.ffcp0", true, "", makeF16(0, 0, 0x3ff)},
- {"0x1.800p0", true, "", makeF16(0, 0, 0x200)},
- {"0x1.c00p0", true, "", makeF16(0, 0, 0x300)},
- {"0x1.e00p0", true, "", makeF16(0, 0, 0x380)},
- {"0x1.f00p0", true, "", makeF16(0, 0, 0x3c0)},
- {"0x1.f80p0", true, "", makeF16(0, 0, 0x3e0)},
- {"0x1.fc0p0", true, "", makeF16(0, 0, 0x3f0)},
- {"0x1.fe0p0", true, "", makeF16(0, 0, 0x3f8)},
- {"0x1.ff0p0", true, "", makeF16(0, 0, 0x3fc)},
- {"0x1.ff8p0", true, "", makeF16(0, 0, 0x3fe)},
- {"0x1.ffcp0", true, "", makeF16(0, 0, 0x3ff)},
- // Add several trailing zeros
- {"0x1.c00000p0", true, "", makeF16(0, 0, 0x300)},
- {"0x1.e00000p0", true, "", makeF16(0, 0, 0x380)},
- {"0x1.f00000p0", true, "", makeF16(0, 0, 0x3c0)},
- {"0x1.f80000p0", true, "", makeF16(0, 0, 0x3e0)},
- {"0x1.fc0000p0", true, "", makeF16(0, 0, 0x3f0)},
- {"0x1.fe0000p0", true, "", makeF16(0, 0, 0x3f8)},
- {"0x1.ff0000p0", true, "", makeF16(0, 0, 0x3fc)},
- {"0x1.ff8000p0", true, "", makeF16(0, 0, 0x3fe)},
- {"0x1.ffcp0000", true, "", makeF16(0, 0, 0x3ff)},
- // Samples that drop out bits in the middle.
- // 5 = 0101 4 = 0100
- // a = 1010 8 = 1000
- {"0x1.5a4p0", true, "", makeF16(0, 0, 0x169)},
- {"0x1.a58p0", true, "", makeF16(0, 0, 0x296)},
- // Samples that drop out bits *and* truncate significant bits
- // that can't be represented.
- {"0x1.5a40000p0", true, "", makeF16(0, 0, 0x169)},
- {"0x1.5a7ffffp0", true, "", makeF16(0, 0, 0x169)},
- {"0x1.a580000p0", true, "", makeF16(0, 0, 0x296)},
- {"0x1.a5bffffp0", true, "", makeF16(0, 0, 0x296)},
- // Try some negations.
- {"-0x0p0", true, "", makeF16(1, -15, 0x0)},
- {"-0x000.0000p0", true, "", makeF16(1, -15, 0x0)},
- {"-0x1.5a40000p0", true, "", makeF16(1, 0, 0x169)},
- {"-0x1.5a7ffffp0", true, "", makeF16(1, 0, 0x169)},
- {"-0x1.a580000p0", true, "", makeF16(1, 0, 0x296)},
- {"-0x1.a5bffffp0", true, "", makeF16(1, 0, 0x296)}}));
- INSTANTIATE_TEST_SUITE_P(
- HexFloat16IncreasingExponentsAndMantissa, Float16StreamParseTest,
- ::testing::ValuesIn(std::vector<StreamParseCase<Float16>>{
- // Zero
- {"0x0p0", true, "", makeF16(0, -15, 0x0)},
- {"0x0p5000000000000", true, "", makeF16(0, -15, 0x0)},
- {"-0x0p5000000000000", true, "", makeF16(1, -15, 0x0)},
- // Leading 1
- {"0x1p0", true, "", makeF16(0, 0, 0x0)},
- {"0x1p1", true, "", makeF16(0, 1, 0x0)},
- {"0x1p16", true, "", makeF16(0, 16, 0x0)},
- {"0x1p-1", true, "", makeF16(0, -1, 0x0)},
- {"0x1p-14", true, "", makeF16(0, -14, 0x0)},
- // Leading 2
- {"0x2p0", true, "", makeF16(0, 1, 0x0)},
- {"0x2p1", true, "", makeF16(0, 2, 0x0)},
- {"0x2p15", true, "", makeF16(0, 16, 0x0)},
- {"0x2p-1", true, "", makeF16(0, 0, 0x0)},
- {"0x2p-15", true, "", makeF16(0, -14, 0x0)},
- // Leading 8
- {"0x8p0", true, "", makeF16(0, 3, 0x0)},
- {"0x8p1", true, "", makeF16(0, 4, 0x0)},
- {"0x8p13", true, "", makeF16(0, 16, 0x0)},
- {"0x8p-3", true, "", makeF16(0, 0, 0x0)},
- {"0x8p-17", true, "", makeF16(0, -14, 0x0)},
- // Leading 10
- {"0x10.0p0", true, "", makeF16(0, 4, 0x0)},
- {"0x10.0p1", true, "", makeF16(0, 5, 0x0)},
- {"0x10.0p12", true, "", makeF16(0, 16, 0x0)},
- {"0x10.0p-5", true, "", makeF16(0, -1, 0x0)},
- {"0x10.0p-18", true, "", makeF16(0, -14, 0x0)},
- // Samples that drop out bits *and* truncate significant bits
- // that can't be represented.
- // Progressively increase the leading digit.
- {"0x1.5a40000p0", true, "", makeF16(0, 0, 0x169)},
- {"0x1.5a7ffffp0", true, "", makeF16(0, 0, 0x169)},
- {"0x2.5a40000p0", true, "", makeF16(0, 1, 0x0b4)},
- {"0x2.5a7ffffp0", true, "", makeF16(0, 1, 0x0b4)},
- {"0x4.5a40000p0", true, "", makeF16(0, 2, 0x05a)},
- {"0x4.5a7ffffp0", true, "", makeF16(0, 2, 0x05a)},
- {"0x8.5a40000p0", true, "", makeF16(0, 3, 0x02d)},
- {"0x8.5a7ffffp0", true, "", makeF16(0, 3, 0x02d)}}));
- } // namespace
- } // namespace utils
- } // namespace spvtools
|