зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1438212 - Implement mozilla::IsFloat32Representable using an algorithm that handles NaN correctly and doesn't sometimes invoke undefined behavior. r=froydnj
--HG-- extra : rebase_source : b4246ea818046b1e4100b90a3a371a866ea2b098
This commit is contained in:
Родитель
7bbef1fafc
Коммит
701bad47cd
|
@ -8,14 +8,36 @@
|
|||
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
#include <cfloat> // for FLT_MAX
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool
|
||||
IsFloat32Representable(double aFloat32)
|
||||
IsFloat32Representable(double aValue)
|
||||
{
|
||||
float asFloat = static_cast<float>(aFloat32);
|
||||
double floatAsDouble = static_cast<double>(asFloat);
|
||||
return floatAsDouble == aFloat32;
|
||||
// NaNs and infinities are representable.
|
||||
if (!IsFinite(aValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it exceeds finite |float| range, casting to |double| is always undefined
|
||||
// behavior per C++11 [conv.double]p1 last sentence.
|
||||
if (Abs(aValue) > FLT_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// But if it's within finite range, then either it's 1) an exact value and so
|
||||
// representable, or 2) it's "between two adjacent destination values" and
|
||||
// safe to cast to "an implementation-defined choice of either of those
|
||||
// values".
|
||||
auto valueAsFloat = static_cast<float>(aValue);
|
||||
|
||||
// Per [conv.fpprom] this never changes value.
|
||||
auto valueAsFloatAsDouble = static_cast<double>(valueAsFloat);
|
||||
|
||||
// Finally, in 1) exact representable value equals exact representable value,
|
||||
// or 2) *changed* value does not equal original value, ergo unrepresentable.
|
||||
return valueAsFloatAsDouble == aValue;
|
||||
}
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
|
|
@ -562,16 +562,14 @@ FuzzyEqualsMultiplicative(T aValue1, T aValue2,
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value can be losslessly represented as an IEEE-754
|
||||
* single format number, false otherwise. All NaN values are considered
|
||||
* representable (notwithstanding that the exact bit pattern of a double format
|
||||
* NaN value can't be exactly represented in single format).
|
||||
*
|
||||
* This function isn't inlined to avoid buggy optimizations by MSVC.
|
||||
* Returns true if |aValue| can be losslessly represented as an IEEE-754 single
|
||||
* precision number, false otherwise. All NaN values are considered
|
||||
* representable (even though the bit patterns of double precision NaNs can't
|
||||
* all be exactly represented in single precision).
|
||||
*/
|
||||
MOZ_MUST_USE
|
||||
extern MFBT_API bool
|
||||
IsFloat32Representable(double aFloat32);
|
||||
IsFloat32Representable(double aValue);
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ using mozilla::FloatingPoint;
|
|||
using mozilla::FuzzyEqualsAdditive;
|
||||
using mozilla::FuzzyEqualsMultiplicative;
|
||||
using mozilla::IsFinite;
|
||||
using mozilla::IsFloat32Representable;
|
||||
using mozilla::IsInfinite;
|
||||
using mozilla::IsNaN;
|
||||
using mozilla::IsNegative;
|
||||
|
@ -630,6 +631,80 @@ TestAreApproximatelyEqual()
|
|||
TestDoublesAreApproximatelyEqual();
|
||||
}
|
||||
|
||||
static void
|
||||
TestIsFloat32Representable()
|
||||
{
|
||||
// Zeroes are representable.
|
||||
A(IsFloat32Representable(+0.0));
|
||||
A(IsFloat32Representable(-0.0));
|
||||
|
||||
// NaN and infinities are representable.
|
||||
A(IsFloat32Representable(UnspecifiedNaN<double>()));
|
||||
A(IsFloat32Representable(SpecificNaN<double>(0, 1)));
|
||||
A(IsFloat32Representable(SpecificNaN<double>(0, 71389)));
|
||||
A(IsFloat32Representable(SpecificNaN<double>(0, (uint64_t(1) << 52) - 2)));
|
||||
A(IsFloat32Representable(SpecificNaN<double>(1, 1)));
|
||||
A(IsFloat32Representable(SpecificNaN<double>(1, 71389)));
|
||||
A(IsFloat32Representable(SpecificNaN<double>(1, (uint64_t(1) << 52) - 2)));
|
||||
A(IsFloat32Representable(PositiveInfinity<double>()));
|
||||
A(IsFloat32Representable(NegativeInfinity<double>()));
|
||||
|
||||
// MSVC seems to compile 2**-1075, which should be half of the smallest
|
||||
// IEEE-754 double precision value, to equal 2**-1074 right now. This might
|
||||
// be the result of a missing compiler flag to force more-accurate floating
|
||||
// point calculations; bug 1440184 has been filed as a followup to fix this,
|
||||
// so that only the first half of this condition is necessary.
|
||||
A(pow(2.0, -1075.0) == 0.0 ||
|
||||
(MOZ_IS_MSVC && pow(2.0, -1075.0) == pow(2.0, -1074.0)));
|
||||
|
||||
A(powf(2.0f, -150.0f) == 0.0);
|
||||
A(powf(2.0f, -149.0f) != 0.0);
|
||||
|
||||
for (double littleExp = -1074.0; littleExp < -149.0; littleExp++) {
|
||||
// Powers of two below the available range aren't representable.
|
||||
A(!IsFloat32Representable(pow(2.0, littleExp)));
|
||||
}
|
||||
|
||||
// Exact powers of two within the available range are representable.
|
||||
for (double exponent = -149.0; exponent < 128.0; exponent++) {
|
||||
A(IsFloat32Representable(pow(2.0, exponent)));
|
||||
}
|
||||
|
||||
// Powers of two above the available range aren't representable.
|
||||
for (double bigExp = 128.0; bigExp < 1024.0; bigExp++) {
|
||||
A(!IsFloat32Representable(pow(2.0, bigExp)));
|
||||
}
|
||||
|
||||
// Various denormal (i.e. super-small) doubles with MSB and LSB as far apart
|
||||
// as possible are representable (but taken one bit further apart are not
|
||||
// representable).
|
||||
//
|
||||
// Note that the final iteration tests non-denormal with exponent field
|
||||
// containing (biased) 1, as |oneTooSmall| and |widestPossible| happen still
|
||||
// to be correct for that exponent due to the extra bit of precision in the
|
||||
// implicit-one bit.
|
||||
double oneTooSmall = pow(2.0, -150.0);
|
||||
for (double denormExp = -149.0;
|
||||
denormExp < 1 - double(FloatingPoint<double>::kExponentBias) + 1;
|
||||
denormExp++)
|
||||
{
|
||||
double baseDenorm = pow(2.0, denormExp);
|
||||
double tooWide = baseDenorm + oneTooSmall;
|
||||
A(!IsFloat32Representable(tooWide));
|
||||
|
||||
double widestPossible = baseDenorm;
|
||||
if (oneTooSmall * 2.0 != baseDenorm) {
|
||||
widestPossible += oneTooSmall * 2.0;
|
||||
}
|
||||
|
||||
A(IsFloat32Representable(widestPossible));
|
||||
}
|
||||
|
||||
// Finally, check certain interesting/special values for basic sanity.
|
||||
A(!IsFloat32Representable(2147483647.0));
|
||||
A(!IsFloat32Representable(-2147483647.0));
|
||||
}
|
||||
|
||||
#undef A
|
||||
|
||||
int
|
||||
|
@ -639,5 +714,6 @@ main()
|
|||
TestExponentComponent();
|
||||
TestPredicates();
|
||||
TestAreApproximatelyEqual();
|
||||
TestIsFloat32Representable();
|
||||
return 0;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче