зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1673513 - ProportionValue - r=florian
Class storing a value between 0 and 1, effectively 0% to 100%. It will be used through a ProgressLogger object to track the progress of JSON profile generation (see following patches). Differential Revision: https://phabricator.services.mozilla.com/D135477
This commit is contained in:
Родитель
32bed2a81f
Коммит
cb97352555
|
@ -103,6 +103,7 @@ EXPORTS.mozilla += [
|
|||
"public/ProfileBufferEntrySerialization.h",
|
||||
"public/ProfileBufferIndex.h",
|
||||
"public/ProfileChunkedBuffer.h",
|
||||
"public/ProportionValue.h",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ProportionValue_h
|
||||
#define ProportionValue_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Class storing a proportion value between 0 and 1, effectively 0% to 100%.
|
||||
// The public interface deals with doubles, but internally the value is encoded
|
||||
// in an integral type, so arithmetic operations are fast.
|
||||
// It also supports an invalid value: Use MakeInvalid() to construct, it infects
|
||||
// any operation, and gets converted to a signaling NaN.
|
||||
class ProportionValue {
|
||||
public:
|
||||
using UnderlyingType = uint32_t;
|
||||
|
||||
// Default-construct at 0%.
|
||||
constexpr ProportionValue()
|
||||
// This `noexcept` is necessary to avoid a build error when encapsulating
|
||||
// `ProportionValue` in `std::Atomic`:
|
||||
// "use of deleted function
|
||||
// 'constexpr std::atomic<mozilla::ProportionValue>::atomic()"
|
||||
// because the default `std::atomic<T>::atomic()` constructor is marked:
|
||||
// `noexcept(std::is_nothrow_default_constructible_v<T>)`
|
||||
// and therefore this default constructor here must be explicitly marked
|
||||
// `noexcept` as well.
|
||||
noexcept
|
||||
: mIntegralValue(0u) {}
|
||||
|
||||
// Construct a ProportionValue with the given value, clamped to 0..1.
|
||||
// Note that it's constexpr, so construction from literal numbers should incur
|
||||
// no runtime costs.
|
||||
// If `aValue` is NaN, behavior is undefined! Use `MakeInvalid()` instead.
|
||||
constexpr explicit ProportionValue(double aValue)
|
||||
: mIntegralValue(UnderlyingType(std::clamp(aValue, 0.0, 1.0) * scMaxD)) {}
|
||||
|
||||
[[nodiscard]] static constexpr ProportionValue MakeInvalid() {
|
||||
return ProportionValue(scInvalidU, Internal{});
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr double ToDouble() const {
|
||||
return IsInvalid() ? std::numeric_limits<double>::signaling_NaN()
|
||||
: (double(mIntegralValue) * scInvMaxD);
|
||||
}
|
||||
|
||||
// Retrieve the underlying integral value, for storage or testing purposes.
|
||||
[[nodiscard]] constexpr UnderlyingType ToUnderlyingType() const {
|
||||
return mIntegralValue;
|
||||
};
|
||||
|
||||
// Re-construct a ProportionValue from an underlying integral value.
|
||||
[[nodiscard]] static constexpr ProportionValue FromUnderlyingType(
|
||||
UnderlyingType aUnderlyingType) {
|
||||
return ProportionValue(
|
||||
(aUnderlyingType <= scMaxU) ? aUnderlyingType : scInvalidU, Internal{});
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsExactlyZero() const {
|
||||
return mIntegralValue == 0u;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsExactlyOne() const {
|
||||
return mIntegralValue == scMaxU;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsValid() const {
|
||||
// Compare to the maximum value, not just exactly scInvalidU, to catch any
|
||||
// kind of invalid state.
|
||||
return mIntegralValue <= scMaxU;
|
||||
}
|
||||
[[nodiscard]] constexpr bool IsInvalid() const {
|
||||
// Compare to the maximum value, not just exactly scInvalidU, to catch any
|
||||
// kind of invalid state.
|
||||
return mIntegralValue > scMaxU;
|
||||
}
|
||||
|
||||
// Strict comparisons based on the underlying integral value. Use
|
||||
// `CompareWithin` instead to make fuzzy comparisons.
|
||||
// `ProportionValue::MakeInvalid()`s are equal, and greater than anything
|
||||
// else; Best to avoid comparisons, and first use IsInvalid() instead.
|
||||
#define OPERATOR_COMPARISON(CMP) \
|
||||
[[nodiscard]] constexpr friend bool operator CMP( \
|
||||
const ProportionValue& aLHS, const ProportionValue& aRHS) { \
|
||||
return aLHS.mIntegralValue CMP aRHS.mIntegralValue; \
|
||||
}
|
||||
OPERATOR_COMPARISON(==)
|
||||
OPERATOR_COMPARISON(!=)
|
||||
OPERATOR_COMPARISON(<)
|
||||
OPERATOR_COMPARISON(<=)
|
||||
OPERATOR_COMPARISON(>)
|
||||
OPERATOR_COMPARISON(>=)
|
||||
#undef OPERATOR_COMPARISON
|
||||
|
||||
// Arithmetic operations + - *, all working on the underlying integral values
|
||||
// (i.e, no expensive floating-point operations are used), and always clamping
|
||||
// to 0..1 range. Invalid values are poisonous.
|
||||
|
||||
[[nodiscard]] constexpr ProportionValue operator+(
|
||||
ProportionValue aRHS) const {
|
||||
return ProportionValue(
|
||||
(IsInvalid() || aRHS.IsInvalid())
|
||||
? scInvalidU
|
||||
// Adding fixed-point values keep the same scale, so there is no
|
||||
// adjustment needed for that. [0,1]+[0,1]=[0,2], so we only need to
|
||||
// ensure that the result is capped at max 1, aka scMaxU:
|
||||
// a+b<=max <=> b<=max-a, so b is at maximum max-a.
|
||||
: (mIntegralValue +
|
||||
std::min(aRHS.mIntegralValue, scMaxU - mIntegralValue)),
|
||||
Internal{});
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr ProportionValue operator-(
|
||||
ProportionValue aRHS) const {
|
||||
return ProportionValue(
|
||||
(IsInvalid() || aRHS.IsInvalid())
|
||||
? scInvalidU
|
||||
// Subtracting fixed-point values keep the same scale, so there is
|
||||
// no adjustment needed for that. [0,1]-[0,1]=[-1,1], so we only
|
||||
// need to ensure that the value is positive:
|
||||
// a-b>=0 <=> b<=a, so b is at maximum a.
|
||||
: (mIntegralValue - std::min(aRHS.mIntegralValue, mIntegralValue)),
|
||||
Internal{});
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr ProportionValue operator*(
|
||||
ProportionValue aRHS) const {
|
||||
// Type to hold the full result of multiplying two maximum numbers.
|
||||
using DoublePrecisionType = uint64_t;
|
||||
static_assert(sizeof(DoublePrecisionType) >= 2 * sizeof(UnderlyingType));
|
||||
return ProportionValue(
|
||||
(IsInvalid() || aRHS.IsInvalid())
|
||||
? scInvalidU
|
||||
// Multiplying fixed-point values doubles the scale (2^31 -> 2^62),
|
||||
// so we need to adjust the result by dividing it by one scale
|
||||
// (which is optimized into a binary right-shift).
|
||||
: (UnderlyingType((DoublePrecisionType(mIntegralValue) *
|
||||
DoublePrecisionType(aRHS.mIntegralValue)) /
|
||||
DoublePrecisionType(scMaxU))),
|
||||
Internal{});
|
||||
}
|
||||
|
||||
// Explicitly forbid divisions, they make little sense, and would almost
|
||||
// always return a clamped 100% (E.g.: 50% / 10% = 0.5 / 0.1 = 5 = 500%).
|
||||
[[nodiscard]] constexpr ProportionValue operator/(
|
||||
ProportionValue aRHS) const = delete;
|
||||
|
||||
// Division by a positive integer value, useful to split an interval in equal
|
||||
// parts (with maybe some spare space at the end, because it is rounded down).
|
||||
// Division by 0 produces an invalid value.
|
||||
[[nodiscard]] constexpr ProportionValue operator/(uint32_t aDivisor) const {
|
||||
return ProportionValue((IsInvalid() || aDivisor == 0u)
|
||||
? scInvalidU
|
||||
: (mIntegralValue / aDivisor),
|
||||
Internal{});
|
||||
}
|
||||
|
||||
// Multiplication by a positive integer value, useful as inverse of the
|
||||
// integer division above. But it may be lossy because the division is rounded
|
||||
// down, therefore: PV - u < (PV / u) * u <= PV.
|
||||
// Clamped to 100% max.
|
||||
[[nodiscard]] constexpr ProportionValue operator*(
|
||||
uint32_t aMultiplier) const {
|
||||
return ProportionValue(IsInvalid()
|
||||
? scInvalidU
|
||||
: ((aMultiplier > scMaxU / mIntegralValue)
|
||||
? scMaxU
|
||||
: (mIntegralValue * aMultiplier)),
|
||||
Internal{});
|
||||
}
|
||||
|
||||
private:
|
||||
// Tagged constructor for internal construction from the UnderlyingType, so
|
||||
// that it is never ambiguously considered in constructions from one number.
|
||||
struct Internal {};
|
||||
constexpr ProportionValue(UnderlyingType aIntegralValue, Internal)
|
||||
: mIntegralValue(aIntegralValue) {}
|
||||
|
||||
// Use all but 1 bit for the fractional part.
|
||||
// Valid values can go from 0b0 (0%) up to 0b1000...00 (scMaxU aka 100%).
|
||||
static constexpr unsigned scFractionalBits = sizeof(UnderlyingType) * 8 - 1;
|
||||
// Maximum value corresponding to 1.0 or 100%.
|
||||
static constexpr UnderlyingType scMaxU = UnderlyingType(1u)
|
||||
<< scFractionalBits;
|
||||
// This maximum value corresponding to 1.0 can also be seen as the scaling
|
||||
// factor from any [0,1] `double` value to the internal integral value.
|
||||
static constexpr double scMaxD = double(scMaxU);
|
||||
// The inverse can be used to convert the internal value back to [0,1].
|
||||
static constexpr double scInvMaxD = 1.0 / scMaxD;
|
||||
|
||||
// Special value outside [0,max], used to construct invalid values.
|
||||
static constexpr UnderlyingType scInvalidU = ~UnderlyingType(0u);
|
||||
|
||||
// Internal integral value, guaranteed to always be <= scMaxU, or scInvalidU.
|
||||
// This is effectively a fixed-point value using 1 bit for the integer part
|
||||
// and 31 bits for the fractional part.
|
||||
// It is roughly equal to the `double` value [0,1] multiplied by scMaxD.
|
||||
UnderlyingType mIntegralValue;
|
||||
};
|
||||
|
||||
namespace literals {
|
||||
inline namespace ProportionValue_literals {
|
||||
|
||||
// User-defined literal for integer percentages, e.g.: `10_pc`, `100_pc`
|
||||
// (equivalent to `ProportionValue{0.1}` and `ProportionValue{1.0}`).
|
||||
// Clamped to [0, 100]_pc.
|
||||
[[nodiscard]] constexpr ProportionValue operator""_pc(
|
||||
unsigned long long int aPercentage) {
|
||||
return ProportionValue{
|
||||
double(std::clamp<unsigned long long int>(aPercentage, 0u, 100u)) /
|
||||
100.0};
|
||||
}
|
||||
|
||||
// User-defined literal for non-integer percentages, e.g.: `12.3_pc`, `100.0_pc`
|
||||
// (equivalent to `ProportionValue{0.123}` and `ProportionValue{1.0}`).
|
||||
// Clamped to [0.0, 100.0]_pc.
|
||||
[[nodiscard]] constexpr ProportionValue operator""_pc(long double aPercentage) {
|
||||
return ProportionValue{
|
||||
double(std::clamp<long double>(aPercentage, 0.0, 100.0)) / 100.0};
|
||||
}
|
||||
|
||||
} // namespace ProportionValue_literals
|
||||
} // namespace literals
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ProportionValue_h
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BaseProfileJSONWriter.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/ProportionValue.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
# include "mozilla/BaseProfilerMarkerTypes.h"
|
||||
|
@ -226,6 +228,203 @@ void TestProfilerUtils() {
|
|||
printf("TestProfilerUtils done\n");
|
||||
}
|
||||
|
||||
void TestProportionValue() {
|
||||
printf("TestProportionValue...\n");
|
||||
|
||||
using mozilla::ProportionValue;
|
||||
|
||||
#define STATIC_ASSERT_EQ(a, b) \
|
||||
static_assert((a) == (b)); \
|
||||
MOZ_RELEASE_ASSERT((a) == (b));
|
||||
|
||||
#define STATIC_ASSERT(e) STATIC_ASSERT_EQ(e, true)
|
||||
|
||||
// Conversion from&to double.
|
||||
STATIC_ASSERT_EQ(ProportionValue().ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ(ProportionValue(0.0).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ(ProportionValue(0.5).ToDouble(), 0.5);
|
||||
STATIC_ASSERT_EQ(ProportionValue(1.0).ToDouble(), 1.0);
|
||||
|
||||
// Clamping.
|
||||
STATIC_ASSERT_EQ(
|
||||
ProportionValue(std::numeric_limits<double>::min()).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ(
|
||||
ProportionValue(std::numeric_limits<long double>::min()).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ(ProportionValue(-1.0).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ(ProportionValue(-0.01).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ(ProportionValue(-0.0).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ(ProportionValue(1.01).ToDouble(), 1.0);
|
||||
STATIC_ASSERT_EQ(
|
||||
ProportionValue(std::numeric_limits<double>::max()).ToDouble(), 1.0);
|
||||
|
||||
// User-defined literal.
|
||||
{
|
||||
using namespace mozilla::literals::ProportionValue_literals;
|
||||
STATIC_ASSERT_EQ(0_pc, ProportionValue(0.0));
|
||||
STATIC_ASSERT_EQ(0._pc, ProportionValue(0.0));
|
||||
STATIC_ASSERT_EQ(50_pc, ProportionValue(0.5));
|
||||
STATIC_ASSERT_EQ(50._pc, ProportionValue(0.5));
|
||||
STATIC_ASSERT_EQ(100_pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(100._pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(101_pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(100.01_pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(1000_pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(1000._pc, ProportionValue(1.0));
|
||||
}
|
||||
{
|
||||
// ProportionValue_literals is an inline namespace of mozilla::literals, so
|
||||
// it's optional.
|
||||
using namespace mozilla::literals;
|
||||
STATIC_ASSERT_EQ(0_pc, ProportionValue(0.0));
|
||||
STATIC_ASSERT_EQ(0._pc, ProportionValue(0.0));
|
||||
STATIC_ASSERT_EQ(50_pc, ProportionValue(0.5));
|
||||
STATIC_ASSERT_EQ(50._pc, ProportionValue(0.5));
|
||||
STATIC_ASSERT_EQ(100_pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(100._pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(101_pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(100.01_pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(1000_pc, ProportionValue(1.0));
|
||||
STATIC_ASSERT_EQ(1000._pc, ProportionValue(1.0));
|
||||
}
|
||||
|
||||
// Invalid construction, conversion to double NaN.
|
||||
MOZ_RELEASE_ASSERT(mozilla::IsNaN(ProportionValue::MakeInvalid().ToDouble()));
|
||||
|
||||
using namespace mozilla::literals::ProportionValue_literals;
|
||||
|
||||
// Conversion to&from underlying integral number.
|
||||
STATIC_ASSERT_EQ(
|
||||
ProportionValue::FromUnderlyingType((0_pc).ToUnderlyingType()).ToDouble(),
|
||||
0.0);
|
||||
STATIC_ASSERT_EQ(
|
||||
ProportionValue::FromUnderlyingType((50_pc).ToUnderlyingType())
|
||||
.ToDouble(),
|
||||
0.5);
|
||||
STATIC_ASSERT_EQ(
|
||||
ProportionValue::FromUnderlyingType((100_pc).ToUnderlyingType())
|
||||
.ToDouble(),
|
||||
1.0);
|
||||
STATIC_ASSERT(ProportionValue::FromUnderlyingType(
|
||||
ProportionValue::MakeInvalid().ToUnderlyingType())
|
||||
.IsInvalid());
|
||||
|
||||
// IsExactlyZero.
|
||||
STATIC_ASSERT(ProportionValue().IsExactlyZero());
|
||||
STATIC_ASSERT((0_pc).IsExactlyZero());
|
||||
STATIC_ASSERT(!(50_pc).IsExactlyZero());
|
||||
STATIC_ASSERT(!(100_pc).IsExactlyZero());
|
||||
STATIC_ASSERT(!ProportionValue::MakeInvalid().IsExactlyZero());
|
||||
|
||||
// IsExactlyOne.
|
||||
STATIC_ASSERT(!ProportionValue().IsExactlyOne());
|
||||
STATIC_ASSERT(!(0_pc).IsExactlyOne());
|
||||
STATIC_ASSERT(!(50_pc).IsExactlyOne());
|
||||
STATIC_ASSERT((100_pc).IsExactlyOne());
|
||||
STATIC_ASSERT(!ProportionValue::MakeInvalid().IsExactlyOne());
|
||||
|
||||
// IsValid.
|
||||
STATIC_ASSERT(ProportionValue().IsValid());
|
||||
STATIC_ASSERT((0_pc).IsValid());
|
||||
STATIC_ASSERT((50_pc).IsValid());
|
||||
STATIC_ASSERT((100_pc).IsValid());
|
||||
STATIC_ASSERT(!ProportionValue::MakeInvalid().IsValid());
|
||||
|
||||
// IsInvalid.
|
||||
STATIC_ASSERT(!ProportionValue().IsInvalid());
|
||||
STATIC_ASSERT(!(0_pc).IsInvalid());
|
||||
STATIC_ASSERT(!(50_pc).IsInvalid());
|
||||
STATIC_ASSERT(!(100_pc).IsInvalid());
|
||||
STATIC_ASSERT(ProportionValue::MakeInvalid().IsInvalid());
|
||||
|
||||
// Addition.
|
||||
STATIC_ASSERT_EQ((0_pc + 0_pc).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ((0_pc + 100_pc).ToDouble(), 1.0);
|
||||
STATIC_ASSERT_EQ((100_pc + 0_pc).ToDouble(), 1.0);
|
||||
STATIC_ASSERT_EQ((100_pc + 100_pc).ToDouble(), 1.0);
|
||||
STATIC_ASSERT((ProportionValue::MakeInvalid() + 50_pc).IsInvalid());
|
||||
STATIC_ASSERT((50_pc + ProportionValue::MakeInvalid()).IsInvalid());
|
||||
|
||||
// Subtraction.
|
||||
STATIC_ASSERT_EQ((0_pc - 0_pc).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ((0_pc - 100_pc).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ((100_pc - 0_pc).ToDouble(), 1.0);
|
||||
STATIC_ASSERT_EQ((100_pc - 100_pc).ToDouble(), 0.0);
|
||||
STATIC_ASSERT((ProportionValue::MakeInvalid() - 50_pc).IsInvalid());
|
||||
STATIC_ASSERT((50_pc - ProportionValue::MakeInvalid()).IsInvalid());
|
||||
|
||||
// Multiplication.
|
||||
STATIC_ASSERT_EQ((0_pc * 0_pc).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ((0_pc * 100_pc).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ((50_pc * 50_pc).ToDouble(), 0.25);
|
||||
STATIC_ASSERT_EQ((50_pc * 100_pc).ToDouble(), 0.5);
|
||||
STATIC_ASSERT_EQ((100_pc * 50_pc).ToDouble(), 0.5);
|
||||
STATIC_ASSERT_EQ((100_pc * 0_pc).ToDouble(), 0.0);
|
||||
STATIC_ASSERT_EQ((100_pc * 100_pc).ToDouble(), 1.0);
|
||||
STATIC_ASSERT((ProportionValue::MakeInvalid() * 50_pc).IsInvalid());
|
||||
STATIC_ASSERT((50_pc * ProportionValue::MakeInvalid()).IsInvalid());
|
||||
|
||||
// Division by a positive integer value.
|
||||
STATIC_ASSERT_EQ((100_pc / 1u).ToDouble(), 1.0);
|
||||
STATIC_ASSERT_EQ((100_pc / 2u).ToDouble(), 0.5);
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(6u) / 2u).ToUnderlyingType(), 3u);
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(5u) / 2u).ToUnderlyingType(), 2u);
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(1u) / 2u).ToUnderlyingType(), 0u);
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(0u) / 2u).ToUnderlyingType(), 0u);
|
||||
STATIC_ASSERT((100_pc / 0u).IsInvalid());
|
||||
STATIC_ASSERT((ProportionValue::MakeInvalid() / 2u).IsInvalid());
|
||||
|
||||
// Multiplication by a positive integer value.
|
||||
STATIC_ASSERT_EQ((100_pc * 1u).ToDouble(), 1.0);
|
||||
STATIC_ASSERT_EQ((50_pc * 1u).ToDouble(), 0.5);
|
||||
STATIC_ASSERT_EQ((50_pc * 2u).ToDouble(), 1.0);
|
||||
STATIC_ASSERT_EQ((50_pc * 3u).ToDouble(), 1.0); // Clamped.
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(1u) * 2u).ToUnderlyingType(), 2u);
|
||||
STATIC_ASSERT((ProportionValue::MakeInvalid() * 2u).IsInvalid());
|
||||
|
||||
// Verifying PV - u < (PV / u) * u <= PV, with n=3, PV between 6 and 9 :
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(6u) / 3u).ToUnderlyingType(), 2u);
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(7u) / 3u).ToUnderlyingType(), 2u);
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(8u) / 3u).ToUnderlyingType(), 2u);
|
||||
STATIC_ASSERT_EQ(
|
||||
(ProportionValue::FromUnderlyingType(9u) / 3u).ToUnderlyingType(), 3u);
|
||||
|
||||
// Direct comparisons.
|
||||
STATIC_ASSERT_EQ(0_pc, 0_pc);
|
||||
STATIC_ASSERT(0_pc == 0_pc);
|
||||
STATIC_ASSERT(!(0_pc == 100_pc));
|
||||
STATIC_ASSERT(0_pc != 100_pc);
|
||||
STATIC_ASSERT(!(0_pc != 0_pc));
|
||||
STATIC_ASSERT(0_pc < 100_pc);
|
||||
STATIC_ASSERT(!(0_pc < 0_pc));
|
||||
STATIC_ASSERT(0_pc <= 0_pc);
|
||||
STATIC_ASSERT(0_pc <= 100_pc);
|
||||
STATIC_ASSERT(!(100_pc <= 0_pc));
|
||||
STATIC_ASSERT(100_pc > 0_pc);
|
||||
STATIC_ASSERT(!(100_pc > 100_pc));
|
||||
STATIC_ASSERT(100_pc >= 0_pc);
|
||||
STATIC_ASSERT(100_pc >= 100_pc);
|
||||
STATIC_ASSERT(!(0_pc >= 100_pc));
|
||||
// 0.5 is binary-friendly, so we can double it and compare it exactly.
|
||||
STATIC_ASSERT_EQ(50_pc + 50_pc, 100_pc);
|
||||
|
||||
#undef STATIC_ASSERT_EQ
|
||||
|
||||
printf("TestProportionValue done\n");
|
||||
}
|
||||
|
||||
template <typename Arg0, typename... Args>
|
||||
bool AreAllEqual(Arg0&& aArg0, Args&&... aArgs) {
|
||||
return ((aArg0 == aArgs) && ...);
|
||||
}
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
||||
MOZ_MAYBE_UNUSED static void SleepMilli(unsigned aMilliseconds) {
|
||||
|
@ -4748,6 +4947,7 @@ int main()
|
|||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
TestProfilerUtils();
|
||||
TestProportionValue();
|
||||
// Note that there are two `TestProfiler{,Markers}` functions above, depending
|
||||
// on whether MOZ_GECKO_PROFILER is #defined.
|
||||
TestProfiler();
|
||||
|
|
Загрузка…
Ссылка в новой задаче