mirror of https://github.com/mosra/magnum.git
Browse Source
Most of the code is in the actual test where I'm comparing and benchmarking three different implementations (a naive/straightforward/ground-truth one, the chosen one and a fast though cache-spilling table-based one) to ensure the behavior is consistent across all of them and that the performance is within reasonable bounds. The Corrade::TestSuite benchmarking stuff needs serious improvements, though.pull/190/head
8 changed files with 629 additions and 1 deletions
@ -0,0 +1,104 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Packing.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
union FloatBits { |
||||||
|
UnsignedInt u; |
||||||
|
Float f; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/* half_to_float_fast4() from https://gist.github.com/rygorous/2144712 */ |
||||||
|
Float unpackHalf(const UnsignedShort value) { |
||||||
|
constexpr const FloatBits Magic{113 << 23}; |
||||||
|
/* Exponent mask after shift */ |
||||||
|
constexpr const UnsignedInt ShiftedExp = 0x7c00 << 13; |
||||||
|
|
||||||
|
const UnsignedShort h{value}; |
||||||
|
FloatBits o; |
||||||
|
|
||||||
|
o.u = (h & 0x7fff) << 13; /* exponent/mantissa bits */ |
||||||
|
const UnsignedInt exp = ShiftedExp & o.u; /* just the exponent */ |
||||||
|
o.u += (127 - 15) << 23; /* exponent adjust */ |
||||||
|
|
||||||
|
/* handle exponent special cases */ |
||||||
|
if(exp == ShiftedExp) { /* Inf/NaN? */ |
||||||
|
o.u += (128 - 16) << 23; /* Extra exp adjust */ |
||||||
|
} else if(exp == 0) { /* Zero/Denormal */ |
||||||
|
o.u += 1 << 23; /* Extra exp adjust */ |
||||||
|
o.f -= Magic.f; /* Renormalize */ |
||||||
|
} |
||||||
|
|
||||||
|
o.u |= (h & 0x8000) << 16; /* sign bit */ |
||||||
|
return o.f; |
||||||
|
} |
||||||
|
|
||||||
|
/* float_to_half_fast3() from https://gist.github.com/rygorous/2156668 */ |
||||||
|
UnsignedShort packHalf(const Float value) { |
||||||
|
constexpr const FloatBits FloatInfinity{255 << 23}; |
||||||
|
constexpr const FloatBits HalfInfinity{31 << 23}; |
||||||
|
constexpr const FloatBits Magic{15 << 23}; |
||||||
|
constexpr const UnsignedInt SignMask = 0x80000000u; |
||||||
|
constexpr const UnsignedInt RoundMask = ~0xfffu; |
||||||
|
|
||||||
|
FloatBits f; |
||||||
|
f.f = value; |
||||||
|
UnsignedShort h; |
||||||
|
|
||||||
|
const UnsignedInt sign = f.u & SignMask; |
||||||
|
f.u ^= sign; |
||||||
|
|
||||||
|
/* Note: all the integer compares in this function can be safely compiled
|
||||||
|
into signed compares since all operands are below 0x80000000. Important |
||||||
|
if you want fast straight SSE2 code (since there's no unsigned PCMPGTD). */ |
||||||
|
|
||||||
|
/* Inf or NaN (all exponent bits set): NaN->qNaN and Inf->Inf */ |
||||||
|
if(f.u >= FloatInfinity.u) { |
||||||
|
h = (f.u > FloatInfinity.u) ? 0x7e00 : 0x7c00; |
||||||
|
|
||||||
|
/* (De)normalized number or zero */ |
||||||
|
} else { |
||||||
|
f.u &= RoundMask; |
||||||
|
f.f *= Magic.f; |
||||||
|
f.u -= RoundMask; |
||||||
|
|
||||||
|
/* Clamp to signed infinity if overflowed */ |
||||||
|
if (f.u > HalfInfinity.u) f.u = HalfInfinity.u; |
||||||
|
|
||||||
|
/* Take the bits! */ |
||||||
|
h = f.u >> 13; |
||||||
|
} |
||||||
|
|
||||||
|
h |= sign >> 16; |
||||||
|
return h; |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
@ -0,0 +1,472 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
|
||||||
|
#include "Magnum/Math/Packing.h" |
||||||
|
#include "Magnum/Math/Vector3.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { namespace Test { |
||||||
|
|
||||||
|
struct HalfTest: Corrade::TestSuite::Tester { |
||||||
|
explicit HalfTest(); |
||||||
|
|
||||||
|
void unpack(); |
||||||
|
void pack(); |
||||||
|
void repack(); |
||||||
|
|
||||||
|
void unpack1k(); |
||||||
|
void unpack1kNaive(); |
||||||
|
void unpack1kTable(); |
||||||
|
void pack1k(); |
||||||
|
void pack1kNaive(); |
||||||
|
void pack1kTable(); |
||||||
|
|
||||||
|
private: |
||||||
|
/* Naive / ground-truth packing helpers */ |
||||||
|
UnsignedShort packNaive(Float value); |
||||||
|
Float unpackNaive(UnsignedShort value); |
||||||
|
|
||||||
|
/* Table-based packing helpers */ |
||||||
|
UnsignedInt convertMantissa(UnsignedInt i); |
||||||
|
UnsignedShort packTable(Float value); |
||||||
|
Float unpackTable(UnsignedShort value); |
||||||
|
|
||||||
|
UnsignedInt _mantissaTable[2048]; |
||||||
|
UnsignedInt _exponentTable[64]; |
||||||
|
UnsignedShort _offsetTable[64]; |
||||||
|
UnsignedShort _baseTable[512]; |
||||||
|
UnsignedByte _shiftTable[512]; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef Math::Constants<Float> Constants; |
||||||
|
|
||||||
|
HalfTest::HalfTest() { |
||||||
|
addTests({&HalfTest::unpack, |
||||||
|
&HalfTest::pack}); |
||||||
|
|
||||||
|
addRepeatedTests({&HalfTest::repack}, 65536); |
||||||
|
|
||||||
|
addBenchmarks({ |
||||||
|
&HalfTest::unpack1k, |
||||||
|
&HalfTest::unpack1kNaive, |
||||||
|
&HalfTest::unpack1kTable, |
||||||
|
&HalfTest::pack1k, |
||||||
|
&HalfTest::pack1kNaive, |
||||||
|
&HalfTest::pack1kTable}, 100); |
||||||
|
|
||||||
|
/* Calculate tables for table-based benchmark */ |
||||||
|
_mantissaTable[0] = 0; |
||||||
|
for(std::size_t i = 1; i != 1024; ++i) |
||||||
|
_mantissaTable[i] = convertMantissa(i); |
||||||
|
for(std::size_t i = 1024; i != 2048; ++i) |
||||||
|
_mantissaTable[i] = 0x38000000 + ((i - 1024) << 13); |
||||||
|
|
||||||
|
_exponentTable[0] = 0; |
||||||
|
for(std::size_t i = 1; i != 31; ++i) |
||||||
|
_exponentTable[i] = i << 23; |
||||||
|
_exponentTable[31] = 0x47800000; |
||||||
|
_exponentTable[32] = 0x80000000; |
||||||
|
for(std::size_t i = 33; i != 63; ++i) |
||||||
|
_exponentTable[i] = 0x80000000 + ((i - 32) << 23); |
||||||
|
_exponentTable[63] = 0xc7800000; |
||||||
|
|
||||||
|
for(std::size_t i = 0; i != 64; ++i) |
||||||
|
_offsetTable[i] = 1024; |
||||||
|
_offsetTable[0] = 0; |
||||||
|
_offsetTable[32] = 0; |
||||||
|
|
||||||
|
for(std::int_fast32_t i = 0; i != 256; ++i) { |
||||||
|
std::int_fast32_t e = i - 127; |
||||||
|
if(e < -24) { |
||||||
|
_baseTable[i | 0x000] = 0x0000; |
||||||
|
_baseTable[i | 0x100] = 0x8000; |
||||||
|
_shiftTable[i | 0x000] = 24; |
||||||
|
_shiftTable[i | 0x100] = 24; |
||||||
|
} else if(e < -14) { |
||||||
|
_baseTable[i | 0x000] = (0x0400 >> (-e - 14)); |
||||||
|
_baseTable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000; |
||||||
|
_shiftTable[i | 0x000] = -e - 1; |
||||||
|
_shiftTable[i | 0x100] = -e - 1; |
||||||
|
} else if(e <= 15) { |
||||||
|
_baseTable[i | 0x000] = ((e + 15) << 10); |
||||||
|
_baseTable[i | 0x100] = ((e + 15) << 10) | 0x8000; |
||||||
|
_shiftTable[i | 0x000] = 13; |
||||||
|
_shiftTable[i | 0x100] = 13; |
||||||
|
} else if(e < 128) { |
||||||
|
_baseTable[i | 0x000] = 0x7c00; |
||||||
|
_baseTable[i | 0x100] = 0xfc00; |
||||||
|
_shiftTable[i | 0x000] = 24; |
||||||
|
_shiftTable[i | 0x100] = 24; |
||||||
|
} else { |
||||||
|
_baseTable[i | 0x000] = 0x7c00; |
||||||
|
_baseTable[i | 0x100] = 0xfc00; |
||||||
|
_shiftTable[i | 0x000] = 13; |
||||||
|
_shiftTable[i | 0x100] = 13; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
union FloatBits { |
||||||
|
UnsignedInt u; |
||||||
|
Float f; |
||||||
|
struct { |
||||||
|
UnsignedInt mantissa:23; |
||||||
|
UnsignedInt exponent:8; |
||||||
|
UnsignedInt sign:1; |
||||||
|
} bits; |
||||||
|
}; |
||||||
|
|
||||||
|
union HalfBits { |
||||||
|
UnsignedShort u; |
||||||
|
struct { |
||||||
|
UnsignedShort mantissa:10; |
||||||
|
UnsignedShort exponent:5; |
||||||
|
UnsignedShort sign:1; |
||||||
|
} bits; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/* float_to_half_full() from https://gist.github.com/rygorous/2156668,
|
||||||
|
originally from ISPC */ |
||||||
|
UnsignedShort HalfTest::packNaive(Float value) { |
||||||
|
FloatBits f; |
||||||
|
f.f = value; |
||||||
|
HalfBits o{}; |
||||||
|
|
||||||
|
/* Signed zero/denormal (which will underflow) */ |
||||||
|
if(f.bits.exponent == 0) { |
||||||
|
o.bits.exponent = 0; |
||||||
|
|
||||||
|
/* Inf or NaN (all exponent bits set): NaN->qNaN and Inf->Inf */ |
||||||
|
} else if(f.bits.exponent == 255) { |
||||||
|
o.bits.exponent = 31; |
||||||
|
o.bits.mantissa = f.bits.mantissa ? 0x200 : 0; |
||||||
|
|
||||||
|
/* Normalized number */ |
||||||
|
} else { |
||||||
|
/* Exponent unbias the single, then bias the halfp */ |
||||||
|
Int newexp = f.bits.exponent - 127 + 15; |
||||||
|
|
||||||
|
/* Overflow, return signed infinity */ |
||||||
|
if(newexp >= 31) { |
||||||
|
o.bits.exponent = 31; |
||||||
|
|
||||||
|
/* Underflow */ |
||||||
|
} else if(newexp <= 0) { |
||||||
|
/* Mantissa might be non-zero */ |
||||||
|
if((14 - newexp) <= 24) { |
||||||
|
/* Hidden 1 bit */ |
||||||
|
UnsignedInt mant = f.bits.mantissa | 0x800000; |
||||||
|
o.bits.mantissa = mant >> (14 - newexp); |
||||||
|
|
||||||
|
/* Check for rounding */ |
||||||
|
if((mant >> (13 - newexp)) & 1) { |
||||||
|
/* Round, might overflow into exp bit, but this is OK */ |
||||||
|
o.u++; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
o.bits.exponent = newexp; |
||||||
|
o.bits.mantissa = f.bits.mantissa >> 13; |
||||||
|
|
||||||
|
/* Check for rounding */ |
||||||
|
if(f.bits.mantissa & 0x1000) { |
||||||
|
/* Round, might overflow to inf, this is OK */ |
||||||
|
o.u++; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
o.bits.sign = f.bits.sign; |
||||||
|
return o.u; |
||||||
|
} |
||||||
|
|
||||||
|
/* half_to_float_full() from https://gist.github.com/rygorous/2144712,
|
||||||
|
originally from ISPC */ |
||||||
|
Float HalfTest::unpackNaive(UnsignedShort value) { |
||||||
|
HalfBits h{value}; |
||||||
|
FloatBits o{}; |
||||||
|
|
||||||
|
/* (Signed) zero */ |
||||||
|
if(h.bits.exponent == 0 && h.bits.mantissa == 0) { |
||||||
|
o.bits.sign = h.bits.sign; |
||||||
|
|
||||||
|
} else { |
||||||
|
/* Denormal (will convert to normalized) */ |
||||||
|
if(h.bits.exponent == 0) { |
||||||
|
/* Adjust mantissa so it's normalized (and keep track of exp
|
||||||
|
adjust) */ |
||||||
|
Int e = -1; |
||||||
|
UnsignedInt m = h.bits.mantissa; |
||||||
|
do { |
||||||
|
e++; |
||||||
|
m <<= 1; |
||||||
|
} while((m & 0x400) == 0); |
||||||
|
|
||||||
|
o.bits.mantissa = (m & 0x3ff) << 13; |
||||||
|
o.bits.exponent = 127 - 15 - e; |
||||||
|
o.bits.sign = h.bits.sign; |
||||||
|
|
||||||
|
/* Inf/NaN */ |
||||||
|
} else if(h.bits.exponent == 0x1f) { |
||||||
|
/* Note: it's safe to treat both with the same code path by just
|
||||||
|
truncating lower Mantissa bits in NaNs (this is valid). */ |
||||||
|
o.bits.mantissa = h.bits.mantissa << 13; |
||||||
|
o.bits.exponent = 255; |
||||||
|
o.bits.sign = h.bits.sign; |
||||||
|
|
||||||
|
/* Normalized number */ |
||||||
|
} else { |
||||||
|
o.bits.mantissa = h.bits.mantissa << 13; |
||||||
|
o.bits.exponent = 127 - 15 + h.bits.exponent; |
||||||
|
o.bits.sign = h.bits.sign; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return o.f; |
||||||
|
} |
||||||
|
|
||||||
|
/* Jeroen van der Zijp -- Fast Half Float Conversions, 2008,
|
||||||
|
ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf */
|
||||||
|
UnsignedInt HalfTest::convertMantissa(UnsignedInt i) { |
||||||
|
UnsignedInt m = i << 13; |
||||||
|
UnsignedInt e = 0; |
||||||
|
|
||||||
|
while(!(m & 0x00800000)) { |
||||||
|
e -= 0x00800000; |
||||||
|
m <<= 1; |
||||||
|
} |
||||||
|
|
||||||
|
m &= ~0x00800000; |
||||||
|
e += 0x38800000; |
||||||
|
return m | e; |
||||||
|
} |
||||||
|
|
||||||
|
UnsignedShort HalfTest::packTable(Float value) { |
||||||
|
const UnsignedInt v = reinterpret_cast<const UnsignedInt&>(value); |
||||||
|
return _baseTable[(v >> 23) & 0x1ff] + ((v & 0x007fffff) >> _shiftTable[(v >> 23) & 0x1ff]); |
||||||
|
} |
||||||
|
|
||||||
|
Float HalfTest::unpackTable(UnsignedShort value) { |
||||||
|
UnsignedInt result = _mantissaTable[_offsetTable[value >> 10] + (value & 0x3ff)] + _exponentTable[value >> 10]; |
||||||
|
return reinterpret_cast<Float&>(result); |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::unpack() { |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0x0000), 0.0f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0x3c00), 1.0f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0x4000), 2.0f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0x4200), 3.0f); |
||||||
|
|
||||||
|
CORRADE_COMPARE(unpackNaive(0x0000), 0.0f); |
||||||
|
CORRADE_COMPARE(unpackNaive(0x3c00), 1.0f); |
||||||
|
CORRADE_COMPARE(unpackNaive(0x4000), 2.0f); |
||||||
|
CORRADE_COMPARE(unpackNaive(0x4200), 3.0f); |
||||||
|
|
||||||
|
CORRADE_COMPARE(unpackTable(0x0000), 0.0f); |
||||||
|
CORRADE_COMPARE(unpackTable(0x3c00), 1.0f); |
||||||
|
CORRADE_COMPARE(unpackTable(0x4000), 2.0f); |
||||||
|
CORRADE_COMPARE(unpackTable(0x4200), 3.0f); |
||||||
|
|
||||||
|
/* Normals, denormals, specials */ |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0x8dc2), -0.000351f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0x57bc), 123.75f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0xfe00), -Constants::nan()); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0x7e00), +Constants::nan()); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0xfc00), -Constants::inf()); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(0x7c00), +Constants::inf()); |
||||||
|
|
||||||
|
CORRADE_COMPARE(unpackNaive(0x8dc2), -0.000351f); |
||||||
|
CORRADE_COMPARE(unpackNaive(0x57bc), 123.75f); |
||||||
|
CORRADE_COMPARE(unpackNaive(0xfe00), -Constants::nan()); |
||||||
|
CORRADE_COMPARE(unpackNaive(0x7e00), +Constants::nan()); |
||||||
|
CORRADE_COMPARE(unpackNaive(0xfc00), -Constants::inf()); |
||||||
|
CORRADE_COMPARE(unpackNaive(0x7c00), +Constants::inf()); |
||||||
|
|
||||||
|
CORRADE_COMPARE(unpackTable(0x8dc2), -0.000351f); |
||||||
|
CORRADE_COMPARE(unpackTable(0x57bc), 123.75f); |
||||||
|
CORRADE_COMPARE(unpackTable(0xfe00), -Constants::nan()); |
||||||
|
CORRADE_COMPARE(unpackTable(0x7e00), +Constants::nan()); |
||||||
|
CORRADE_COMPARE(unpackTable(0xfc00), -Constants::inf()); |
||||||
|
CORRADE_COMPARE(unpackTable(0x7c00), +Constants::inf()); |
||||||
|
|
||||||
|
/* Vector */ |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(Math::Vector3<UnsignedShort>{0x0000, 0x4200, 0x3c00}), |
||||||
|
(Math::Vector3<Float>{0.0f, 3.0f, 1.0f})); |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::pack() { |
||||||
|
CORRADE_COMPARE(Math::packHalf(0.0f), 0x0000); |
||||||
|
CORRADE_COMPARE(Math::packHalf(1.0f), 0x3c00); |
||||||
|
CORRADE_COMPARE(Math::packHalf(2.0f), 0x4000); |
||||||
|
CORRADE_COMPARE(Math::packHalf(3.0f), 0x4200); |
||||||
|
|
||||||
|
CORRADE_COMPARE(packNaive(0.0f), 0x0000); |
||||||
|
CORRADE_COMPARE(packNaive(1.0f), 0x3c00); |
||||||
|
CORRADE_COMPARE(packNaive(2.0f), 0x4000); |
||||||
|
CORRADE_COMPARE(packNaive(3.0f), 0x4200); |
||||||
|
|
||||||
|
CORRADE_COMPARE(packTable(0.0f), 0x0000); |
||||||
|
CORRADE_COMPARE(packTable(1.0f), 0x3c00); |
||||||
|
CORRADE_COMPARE(packTable(2.0f), 0x4000); |
||||||
|
CORRADE_COMPARE(packTable(3.0f), 0x4200); |
||||||
|
|
||||||
|
/* Rounding */ |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(Math::packHalf(-1024.01f)), -1024.0f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(Math::packHalf(-1024.50f)), -1025.0f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(Math::packHalf(-1024.99f)), -1025.0f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(Math::packHalf(+1024.01f)), +1024.0f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(Math::packHalf(+1024.50f)), +1025.0f); |
||||||
|
CORRADE_COMPARE(Math::unpackHalf(Math::packHalf(+1024.99f)), +1025.0f); |
||||||
|
|
||||||
|
/* Don't care about rounding behavior of the others */ |
||||||
|
|
||||||
|
/* Normals, denormals, specials */ |
||||||
|
CORRADE_COMPARE(Math::packHalf(-0.000351512f), 0x8dc2); |
||||||
|
CORRADE_COMPARE(Math::packHalf(123.7567f), 0x57bc); |
||||||
|
/* Emscripten doesn't differentiate NaNs and treats their sign slightly
|
||||||
|
differently on different optimization levels. On MSVC they are somehow |
||||||
|
flipped around, so I'm testing w/o the sign. */ |
||||||
|
CORRADE_COMPARE(Math::packHalf(-Constants::nan()) & ~0x8000, 0x7e00); |
||||||
|
CORRADE_COMPARE(Math::packHalf(+Constants::nan()) & ~0x8000, 0x7e00); |
||||||
|
CORRADE_COMPARE(Math::packHalf(-Constants::inf()), 0xfc00); |
||||||
|
CORRADE_COMPARE(Math::packHalf(+Constants::inf()), 0x7c00); |
||||||
|
|
||||||
|
CORRADE_COMPARE(packNaive(-0.000351512f), 0x8dc2); |
||||||
|
CORRADE_COMPARE(packNaive(123.7567f), 0x57bc); |
||||||
|
/* Emscripten doesn't differentiate NaNs and treats their sign slightly
|
||||||
|
differently on different optimization levels. On MSVC they are somehow |
||||||
|
flipped around, so I'm testing w/o the sign. */ |
||||||
|
CORRADE_COMPARE(packNaive(-Constants::nan()) & ~0x8000, 0x7e00); |
||||||
|
CORRADE_COMPARE(packNaive(+Constants::nan()) & ~0x8000, 0x7e00); |
||||||
|
CORRADE_COMPARE(packNaive(-Constants::inf()), 0xfc00); |
||||||
|
CORRADE_COMPARE(packNaive(+Constants::inf()), 0x7c00); |
||||||
|
|
||||||
|
CORRADE_COMPARE(packTable(-0.000351512f), 0x8dc2); |
||||||
|
CORRADE_COMPARE(packTable(123.7567f), 0x57bc); |
||||||
|
/* Emscripten doesn't differentiate NaNs and treats their sign slightly
|
||||||
|
differently on different optimization levels. On MSVC they are somehow |
||||||
|
flipped around, so I'm testing w/o the sign. */ |
||||||
|
CORRADE_COMPARE(packTable(-Constants::nan()) & ~0x8000, 0x7e00); |
||||||
|
CORRADE_COMPARE(packTable(+Constants::nan()) & ~0x8000, 0x7e00); |
||||||
|
CORRADE_COMPARE(packTable(-Constants::inf()), 0xfc00); |
||||||
|
CORRADE_COMPARE(packTable(+Constants::inf()), 0x7c00); |
||||||
|
|
||||||
|
/* Vector */ |
||||||
|
CORRADE_COMPARE(Math::packHalf(Math::Vector3<Float>{0.0f, 3.0f, 1.0f}), |
||||||
|
(Math::Vector3<UnsignedShort>{0x0000, 0x4200, 0x3c00})); |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::repack() { |
||||||
|
UnsignedShort in = testCaseRepeatId(); |
||||||
|
Float result = Math::unpackHalf(in); |
||||||
|
Float resultNaive = unpackNaive(in); |
||||||
|
Float resultTable = unpackTable(in); |
||||||
|
|
||||||
|
/* NaNs don't rountrip, but that's okay */ |
||||||
|
if(result != result) { |
||||||
|
CORRADE_VERIFY(result != result); |
||||||
|
CORRADE_VERIFY(resultNaive != resultNaive); |
||||||
|
CORRADE_VERIFY(resultTable != resultTable); |
||||||
|
|
||||||
|
/* Otherwise verify that both algos give the same results */ |
||||||
|
} else { |
||||||
|
CORRADE_COMPARE(result, resultTable); |
||||||
|
CORRADE_COMPARE(result, resultNaive); |
||||||
|
|
||||||
|
CORRADE_COMPARE(Math::packHalf(result), in); |
||||||
|
CORRADE_COMPARE(packTable(result), in); |
||||||
|
CORRADE_COMPARE(packNaive(result), in); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::pack1k() { |
||||||
|
UnsignedInt out = 0; |
||||||
|
CORRADE_BENCHMARK(100) |
||||||
|
for(std::uint_fast16_t i = 0; i != 1000; ++i) |
||||||
|
out += Math::packHalf(Float(i)*65); |
||||||
|
|
||||||
|
/* To avoid optimizing things out */ |
||||||
|
CORRADE_VERIFY(out); |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::pack1kNaive() { |
||||||
|
UnsignedInt out = 0; |
||||||
|
CORRADE_BENCHMARK(100) |
||||||
|
for(std::uint_fast16_t i = 0; i != 1000; ++i) |
||||||
|
out += packNaive(Float(i)*65); |
||||||
|
|
||||||
|
/* To avoid optimizing things out */ |
||||||
|
CORRADE_VERIFY(out); |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::pack1kTable() { |
||||||
|
UnsignedInt out = 0; |
||||||
|
CORRADE_BENCHMARK(100) |
||||||
|
for(std::uint_fast16_t i = 0; i != 1000; ++i) |
||||||
|
out += packTable(Float(i)*65); |
||||||
|
|
||||||
|
/* To avoid optimizing things out */ |
||||||
|
CORRADE_VERIFY(out); |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::unpack1k() { |
||||||
|
Float out = 0.0f; |
||||||
|
CORRADE_BENCHMARK(100) |
||||||
|
for(std::uint_fast16_t i = 0; i != 1000; ++i) |
||||||
|
out += Math::unpackHalf(i*65); |
||||||
|
|
||||||
|
/* To avoid optimizing things out */ |
||||||
|
CORRADE_VERIFY(out); |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::unpack1kNaive() { |
||||||
|
Float out = 0.0f; |
||||||
|
CORRADE_BENCHMARK(100) |
||||||
|
for(std::uint_fast16_t i = 0; i != 1000; ++i) |
||||||
|
out += unpackNaive(i*65); |
||||||
|
|
||||||
|
/* To avoid optimizing things out */ |
||||||
|
CORRADE_VERIFY(out); |
||||||
|
} |
||||||
|
|
||||||
|
void HalfTest::unpack1kTable() { |
||||||
|
Float out = 0.0f; |
||||||
|
CORRADE_BENCHMARK(100) |
||||||
|
for(std::uint_fast16_t i = 0; i != 1000; ++i) |
||||||
|
out += unpackTable(i*65); |
||||||
|
|
||||||
|
/* To avoid optimizing things out */ |
||||||
|
CORRADE_VERIFY(out); |
||||||
|
} |
||||||
|
|
||||||
|
}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Math::Test::HalfTest) |
||||||
|
|
||||||
Loading…
Reference in new issue