random method
Generate a random FloatingPointValue, using random seed rv in a given
range, if provided. The distribution of values is uniform across the
combined bitfield of exponent and mantissa.
This generates a valid FloatingPointValue anywhere in the range it can
represent:a general FloatingPointValue has a mantissa in [0,2) with 0 <= exponent <= maxExponent().
If genNormal is true, this method will generate normal numbers, and if
genSubNormal is true, it will generate subnormal numbers. If both
are false, an exception will be thrown. Note that the range of numbers
to be generated is respected by these flags, so a range which does not
contain the requested type of number will result in a throw.
You can constrain the range of random numbers generated by using a lower
bound specified by gt or gte and an upper bound specified by lt or
lte. If either both lower or both upper bounds are specified, the
tightest bounds are used.
If excludeInfinity is true, then infinity values will not be
generated. NaN values are never generated.
The range is interpreted as follows:
gt,lt: generate a value in the range([gt], [lt])gte,lt: generate a value in the range[[gte], [lt])gt,lte: generate a value in the range([gt], [lte]]gte,lte: generate a value in the range[[gte], [lte]]gt: generate a value in the range([gt], ∞)gte: generate a value in the range[[gte], ∞)lt: generate a value in the range(-∞, [lt])lte: generate a value in the range(-∞, [lte]]- none: generate a value in the range
(-∞, ∞)
Implementation
FpvType random(Random rv,
{bool genNormal = true,
bool genSubNormal = true,
bool excludeInfinity = false,
FpvType? gt,
FpvType? lt,
FpvType? gte,
FpvType? lte}) {
FpvType cloneConstant(FloatingPointConstants c) =>
_unpopulated.clonePopulator().ofConstant(c) as FpvType;
_checkMatching('gt', gt);
_checkMatching('lt', lt);
_checkMatching('gte', gte);
_checkMatching('lte', lte);
if (gt != null) {
if (lt != null) {
if (gt.compareTo(lt) >= 0) {
throw RohdHclException(
'FloatingPointValuePopulator.random: cannot have $gt >= '
'$lt');
}
} else if (lte != null) {
if (gt.compareTo(lte) > 0) {
throw RohdHclException(
'FloatingPointValuePopulator.random: cannot have $gt > '
'$lte');
}
}
} else if (gte != null) {
if (lt != null) {
if (gte.compareTo(lt) >= 0) {
throw RohdHclException(
'FloatingPointValuePopulator.random: cannot have $gte >= '
'$lt');
}
} else if (lte != null) {
if (gte.compareTo(lte) > 0) {
throw RohdHclException(
'FloatingPointValuePopulator.random: cannot have $gte > '
'$lte');
}
}
}
final doGenNormal = genNormal;
final doGenSubNormal = genSubNormal;
if (!doGenNormal & !doGenSubNormal) {
throw RohdHclException(
'FloatingPointValuePopulator.random: cannot have both genNormal and '
'genSubNormal be false');
}
if (doGenSubNormal & subNormalAsZero) {
throw RohdHclException(
'FloatingPointValuePopulator.random: cannot have doGenSubNormal be '
'true and subNormalAsZero be true');
}
if (explicitJBit) {
FloatingPointValuePopulator populator() => FloatingPointValue.populator(
exponentWidth: exponentWidth, mantissaWidth: mantissaWidth - 1);
return ofFloatingPointValue(populator().random(rv,
genNormal: genNormal,
genSubNormal: genSubNormal,
gt: (gt != null)
? populator().ofFloatingPointValue(gt, canonicalizeExplicit: true)
: null,
lt: (lt != null)
? populator().ofFloatingPointValue(lt, canonicalizeExplicit: true)
: null,
gte: (gte != null)
? populator()
.ofFloatingPointValue(gte, canonicalizeExplicit: true)
: null,
lte: (lte != null)
? populator()
.ofFloatingPointValue(lte, canonicalizeExplicit: true)
: null)) as FpvType;
}
if ((lt == null) & (lte == null)) {
lte = doGenSubNormal & !doGenNormal
? cloneConstant(FloatingPointConstants.largestPositiveSubnormal)
: excludeInfinity
// Use an inclusive bound at the largest finite value instead of
// an exclusive bound at +infinity: this produces the same set
// of generatable values but also works for formats (like
// e4m3) that cannot represent infinity at all.
? cloneConstant(FloatingPointConstants.largestNormal)
: cloneConstant(FloatingPointConstants.positiveInfinity);
}
if ((gt == null) & (gte == null)) {
gte = doGenSubNormal & !doGenNormal
? cloneConstant(FloatingPointConstants.largestPositiveSubnormal)
.negate() as FpvType
: excludeInfinity
? cloneConstant(FloatingPointConstants.largestNormal).negate()
as FpvType
: cloneConstant(FloatingPointConstants.negativeInfinity);
}
// Take the tightest constraints and assign as local variables.
final FpvType? lgte;
final FpvType? lgt;
final FpvType? llte;
final FpvType? llt;
if ((gt != null) && (gte != null)) {
final gtTighter = (gt.compareTo(gte) != -1);
lgte = gtTighter ? null : gte;
lgt = gtTighter ? gt : null;
} else {
lgte = gte;
lgt = gt;
}
if ((lt != null) && (lte != null)) {
final ltTighter = (lt.compareTo(lte) != 1);
llte = ltTighter ? null : lte;
llt = ltTighter ? lt : null;
} else {
llte = lte;
llt = lt;
}
// Trim the limits to skip either normal or subnormal ranges as needed.
// - normal | - subnormal | 0 | + subnormal | + normal
final trimGt = (lgt == null)
? null
: ((lgt.isNormal() & (lgt.sign == LogicValue.one) & !doGenNormal)
? cloneConstant(FloatingPointConstants.smallestPositiveNormal)
.negate() as FpvType
: ((!lgt.isNormal() &
(lgt.sign == LogicValue.one) &
!doGenSubNormal)
? cloneConstant(
FloatingPointConstants.smallestPositiveSubnormal)
.negate() as FpvType
: (!lgt.isNormal() &
(lgt.sign == LogicValue.zero) &
!doGenSubNormal)
? cloneConstant(
FloatingPointConstants.largestPositiveSubnormal)
: lgt));
final trimGte = (lgte == null)
? null
: ((lgte.isNormal() & (lgte.sign == LogicValue.one) & !doGenNormal)
? cloneConstant(FloatingPointConstants.largestPositiveSubnormal)
.negate() as FpvType
: ((!lgte.isNormal() &
(lgte.sign == LogicValue.one) &
!doGenSubNormal)
? cloneConstant(FloatingPointConstants.positiveZero)
: (!lgte.isNormal() &
(lgte.sign == LogicValue.zero) &
!doGenSubNormal)
? cloneConstant(
FloatingPointConstants.smallestPositiveNormal)
: lgte));
final trimLt = (llt == null)
? null
: ((llt.isNormal() & (llt.sign == LogicValue.zero) & !doGenNormal)
? cloneConstant(FloatingPointConstants.smallestPositiveNormal)
: ((!llt.isNormal() & !doGenSubNormal)
? cloneConstant(FloatingPointConstants.largestPositiveSubnormal)
.negate() as FpvType
: llt));
final trimLte = (llte == null)
? null
: ((llte.isNormal() & (llte.sign == LogicValue.zero) & !doGenNormal)
? cloneConstant(FloatingPointConstants.largestPositiveSubnormal)
: ((!llte.isNormal() & !doGenSubNormal)
? cloneConstant(FloatingPointConstants.smallestPositiveNormal)
.negate()
: llte));
final negNormals = doGenNormal &
((trimGt ?? trimGte)!.sign == LogicValue.one) &
((trimGt ?? trimGte)!.isNormal()) &
(trimGt !=
cloneConstant(FloatingPointConstants.smallestPositiveNormal)
.negate());
final posNormals = doGenNormal &
((trimLt ?? trimLte)!.sign == LogicValue.zero) &
((trimLt ?? trimLte)?.isNormal() ?? true) &
(trimLt !=
cloneConstant(FloatingPointConstants.smallestPositiveNormal));
final negSubNormals = doGenSubNormal &
((trimGt ?? trimGte)!.sign == LogicValue.one) &
!(trimGt?.isAZero ?? false) &
!(trimGte?.isAZero ?? false) &
(trimGt !=
cloneConstant(FloatingPointConstants.smallestPositiveSubnormal)
.negate());
final posSubNormals = doGenSubNormal &
((trimLt ?? trimLte)!.sign == LogicValue.zero) &
!(trimLt?.isAZero ?? false) &
!(trimLte?.isAZero ?? false) &
(trimLt !=
cloneConstant(FloatingPointConstants.smallestPositiveSubnormal));
if ((!doGenSubNormal & !negNormals & !posNormals) ||
(!doGenNormal & !negSubNormals & !posSubNormals)) {
throw RohdHclException(
'FloatingPointValuePopulator.random: cannot generate value, '
'range excludes all normal and subnormal values');
}
// Note that we never have both gt/gte or lt/lte null because of Infinity.
var gtSign = (trimGt ?? trimGte)?.sign;
var gtMagnitude =
(trimGt != null) ? [trimGt.exponent, trimGt.mantissa].swizzle() : null;
var gteMagnitude = (trimGte != null)
? [trimGte.exponent, trimGte.mantissa].swizzle()
: null;
var ltSign = (trimLt ?? trimLte)?.sign;
var ltMagnitude =
(trimLt != null) ? [trimLt.exponent, trimLt.mantissa].swizzle() : null;
var lteMagnitude = (trimLte != null)
? [trimLte.exponent, trimLte.mantissa].swizzle()
: null;
if ((!doGenSubNormal) & negNormals & posNormals) {
// We need to pick a side to generate from.
final genSign = rv.nextLogicValue(width: 1);
if (genSign == LogicValue.zero) {
// Generate positive normals.
gtSign = LogicValue.zero;
gtMagnitude = null;
final rhGte =
cloneConstant(FloatingPointConstants.smallestPositiveNormal);
gteMagnitude = [rhGte.exponent, rhGte.mantissa].swizzle();
} else {
// Generate negative normals.
ltSign = LogicValue.one;
ltMagnitude = null;
final lhLte =
cloneConstant(FloatingPointConstants.smallestPositiveNormal)
.negate() as FpvType;
lteMagnitude = [lhLte.exponent, lhLte.mantissa].swizzle();
}
if ((!doGenNormal) & !negSubNormals & !posSubNormals) {
throw RohdHclException(
'FloatingPointValuePopulator.random: cannot generate value, '
'range excludes all normal and subnormal values');
}
} else if ((!doGenNormal) & negSubNormals & posSubNormals) {
// We need to pick a side to generate from.
final genSign = rv.nextLogicValue(width: 1);
if (genSign == LogicValue.zero) {
// Generate positive subnormals.
gtSign = LogicValue.zero;
gtMagnitude = null;
final rhGte = cloneConstant(FloatingPointConstants.positiveZero);
gteMagnitude = [rhGte.exponent, rhGte.mantissa].swizzle();
} else {
// Generate negative subnormals.
ltSign = LogicValue.one;
ltMagnitude = null;
final lhLte = cloneConstant(FloatingPointConstants.negativeZero);
lteMagnitude = [lhLte.exponent, lhLte.mantissa].swizzle();
}
}
final tgt = (gtMagnitude != null)
? SignMagnitudeValue(sign: gtSign!, magnitude: gtMagnitude)
: null;
final tgte = (gteMagnitude != null)
? SignMagnitudeValue(sign: gtSign!, magnitude: gteMagnitude)
: null;
final tlt = (ltMagnitude != null)
? SignMagnitudeValue(sign: ltSign!, magnitude: ltMagnitude)
: null;
final tlte = (lteMagnitude != null)
? SignMagnitudeValue(sign: ltSign!, magnitude: lteMagnitude)
: null;
final smv =
SignMagnitudeValue.populator(width: exponentWidth + mantissaWidth)
.random(rv, gt: tgt, gte: tgte, lt: tlt, lte: tlte);
return populate(
sign: smv.sign,
exponent: smv.magnitude
.slice(exponentWidth + mantissaWidth - 1, mantissaWidth),
mantissa: smv.magnitude.slice(mantissaWidth - 1, 0));
}