#define LLVM_ADT_APFIXEDPOINT_H
#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
/// in the value this represents is treated as padding.
class FixedPointSemantics {
public:
+ static constexpr unsigned WidthBitWidth = 16;
+ static constexpr unsigned LsbWeightBitWidth = 13;
+ /// Used to differentiate between constructors with Width and Lsb from the
+ /// default Width and scale
+ struct Lsb {
+ int LsbWeight;
+ };
FixedPointSemantics(unsigned Width, unsigned Scale, bool IsSigned,
bool IsSaturated, bool HasUnsignedPadding)
- : Width(Width), Scale(Scale), IsSigned(IsSigned),
+ : FixedPointSemantics(Width, Lsb{-static_cast<int>(Scale)}, IsSigned,
+ IsSaturated, HasUnsignedPadding) {}
+ FixedPointSemantics(unsigned Width, Lsb Weight, bool IsSigned,
+ bool IsSaturated, bool HasUnsignedPadding)
+ : Width(Width), LsbWeight(Weight.LsbWeight), IsSigned(IsSigned),
IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) {
- assert(Width >= Scale && "Not enough room for the scale");
+ assert(isUInt<WidthBitWidth>(Width) && isInt<LsbWeightBitWidth>(Weight.LsbWeight));
assert(!(IsSigned && HasUnsignedPadding) &&
"Cannot have unsigned padding on a signed type.");
}
+ /// Check if the Semantic follow the requirements of an older more limited
+ /// version of this class
+ bool isValidLegacySema() const {
+ return LsbWeight <= 0 && Width >= -LsbWeight;
+ }
unsigned getWidth() const { return Width; }
- unsigned getScale() const { return Scale; }
+ unsigned getScale() const { assert(isValidLegacySema()); return -LsbWeight; }
+ int getLsbWeight() const { return LsbWeight; }
+ int getMsbWeight() const {
+ return LsbWeight + Width - 1 /*Both lsb and msb are both part of width*/;
+ }
bool isSigned() const { return IsSigned; }
bool isSaturated() const { return IsSaturated; }
bool hasUnsignedPadding() const { return HasUnsignedPadding; }
void setSaturated(bool Saturated) { IsSaturated = Saturated; }
+ /// return true if the first bit doesn't have a strictly positive weight
+ bool hasSignOrPaddingBit() const { return IsSigned || HasUnsignedPadding; }
+
/// Return the number of integral bits represented by these semantics. These
/// are separate from the fractional bits and do not include the sign or
/// padding bit.
unsigned getIntegralBits() const {
- if (IsSigned || (!IsSigned && HasUnsignedPadding))
- return Width - Scale - 1;
- else
- return Width - Scale;
+ return std::max(getMsbWeight() + 1 - hasSignOrPaddingBit(), 0);
}
/// Return the FixedPointSemantics that allows for calculating the full
FixedPointSemantics
getCommonSemantics(const FixedPointSemantics &Other) const;
+ /// Print semantics for debug purposes
+ void print(llvm::raw_ostream& OS) const;
+
/// Returns true if this fixed-point semantic with its value bits interpreted
/// as an integer can fit in the given floating point semantic without
/// overflowing to infinity.
/*HasUnsignedPadding=*/false);
}
+ bool operator==(FixedPointSemantics Other) const {
+ return Width == Other.Width && LsbWeight == Other.LsbWeight &&
+ IsSigned == Other.IsSigned && IsSaturated == Other.IsSaturated &&
+ HasUnsignedPadding == Other.HasUnsignedPadding;
+ }
+ bool operator!=(FixedPointSemantics Other) const { return !(*this == Other); }
+
private:
- unsigned Width : 16;
- unsigned Scale : 13;
+ unsigned Width : WidthBitWidth;
+ signed int LsbWeight : LsbWeightBitWidth;
unsigned IsSigned : 1;
unsigned IsSaturated : 1;
unsigned HasUnsignedPadding : 1;
};
+static_assert(sizeof(FixedPointSemantics) == 4, "");
+
+inline hash_code hash_value(const FixedPointSemantics &Val) {
+ return hash_value(bit_cast<uint32_t>(Val));
+}
+
+template <> struct DenseMapInfo<FixedPointSemantics> {
+ static inline FixedPointSemantics getEmptyKey() {
+ return FixedPointSemantics(0, 0, false, false, false);
+ }
+
+ static inline FixedPointSemantics getTombstoneKey() {
+ return FixedPointSemantics(0, 1, false, false, false);
+ }
+
+ static unsigned getHashValue(const FixedPointSemantics &Val) {
+ return hash_value(Val);
+ }
+
+ static bool isEqual(const char &LHS, const char &RHS) { return LHS == RHS; }
+};
+
/// The APFixedPoint class works similarly to APInt/APSInt in that it is a
-/// functional replacement for a scaled integer. It is meant to replicate the
-/// fixed point types proposed in ISO/IEC JTC1 SC22 WG14 N1169. The class carries
-/// info about the fixed point type's width, sign, scale, and saturation, and
-/// provides different operations that would normally be performed on fixed point
-/// types.
+/// functional replacement for a scaled integer. It supports a wide range of
+/// semantics including the one used by fixed point types proposed in ISO/IEC
+/// JTC1 SC22 WG14 N1169. The class carries the value and semantics of
+/// a fixed point, and provides different operations that would normally be
+/// performed on fixed point types.
class APFixedPoint {
public:
APFixedPoint(const APInt &Val, const FixedPointSemantics &Sema)
APSInt getValue() const { return APSInt(Val, !Sema.isSigned()); }
inline unsigned getWidth() const { return Sema.getWidth(); }
inline unsigned getScale() const { return Sema.getScale(); }
+ int getLsbWeight() const { return Sema.getLsbWeight(); }
+ int getMsbWeight() const { return Sema.getMsbWeight(); }
inline bool isSaturated() const { return Sema.isSaturated(); }
inline bool isSigned() const { return Sema.isSigned(); }
inline bool hasPadding() const { return Sema.hasUnsignedPadding(); }
/// Return the integral part of this fixed point number, rounded towards
/// zero. (-2.5k -> -2)
APSInt getIntPart() const {
+ if (getMsbWeight() < 0)
+ return APSInt(APInt::getZero(getWidth()), Val.isUnsigned());
+ APSInt ExtVal =
+ (getLsbWeight() > 0) ? Val.extend(getWidth() + getLsbWeight()) : Val;
if (Val < 0 && Val != -Val) // Cover the case when we have the min val
- return -(-Val >> getScale());
- else
- return Val >> getScale();
+ return -(-ExtVal.relativeShl(getLsbWeight()));
+ return ExtVal.relativeShl(getLsbWeight());
}
/// Return the integral part of this fixed point number, rounded towards
return std::string(S.str());
}
+ void print(raw_ostream &) const;
+ void dump() const;
+
// If LHS > RHS, return 1. If LHS == RHS, return 0. If LHS < RHS, return -1.
int compare(const APFixedPoint &Other) const;
bool operator==(const APFixedPoint &Other) const {
return OS;
}
+inline hash_code hash_value(const APFixedPoint &Val) {
+ return hash_combine(Val.getSemantics(), Val.getValue());
+}
+
+template <> struct DenseMapInfo<APFixedPoint> {
+ static inline APFixedPoint getEmptyKey() {
+ return APFixedPoint(DenseMapInfo<FixedPointSemantics>::getEmptyKey());
+ }
+
+ static inline APFixedPoint getTombstoneKey() {
+ return APFixedPoint(DenseMapInfo<FixedPointSemantics>::getTombstoneKey());
+ }
+
+ static unsigned getHashValue(const APFixedPoint &Val) {
+ return hash_value(Val);
+ }
+
+ static bool isEqual(const APFixedPoint &LHS, const APFixedPoint &RHS) {
+ return LHS.getSemantics() == RHS.getSemantics() &&
+ LHS.getValue() == RHS.getValue();
+ }
+};
+
} // namespace llvm
#endif
namespace llvm {
+void FixedPointSemantics::print(llvm::raw_ostream &OS) const {
+ OS << "width=" << getWidth() << ", ";
+ if (isValidLegacySema())
+ OS << "scale=" << getScale() << ", ";
+ OS << "msb=" << getMsbWeight() << ", ";
+ OS << "lsb=" << getLsbWeight() << ", ";
+ OS << "IsSigned=" << IsSigned << ", ";
+ OS << "HasUnsignedPadding=" << HasUnsignedPadding << ", ";
+ OS << "IsSaturated=" << IsSaturated;
+}
+
APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema,
bool *Overflow) const {
APSInt NewVal = Val;
- unsigned DstWidth = DstSema.getWidth();
- unsigned DstScale = DstSema.getScale();
- bool Upscaling = DstScale > getScale();
+ int RelativeUpscale = getLsbWeight() - DstSema.getLsbWeight();
if (Overflow)
*Overflow = false;
- if (Upscaling) {
- NewVal = NewVal.extend(NewVal.getBitWidth() + DstScale - getScale());
- NewVal <<= (DstScale - getScale());
- } else {
- NewVal >>= (getScale() - DstScale);
- }
+ if (RelativeUpscale > 0)
+ NewVal = NewVal.extend(NewVal.getBitWidth() + RelativeUpscale);
+ NewVal = NewVal.relativeShl(RelativeUpscale);
auto Mask = APInt::getBitsSetFrom(
NewVal.getBitWidth(),
- std::min(DstScale + DstSema.getIntegralBits(), NewVal.getBitWidth()));
+ std::min(DstSema.getIntegralBits() - DstSema.getLsbWeight(),
+ NewVal.getBitWidth()));
APInt Masked(NewVal & Mask);
// Change in the bits above the sign
*Overflow = true;
}
- NewVal = NewVal.extOrTrunc(DstWidth);
+ NewVal = NewVal.extOrTrunc(DstSema.getWidth());
NewVal.setIsSigned(DstSema.isSigned());
return APFixedPoint(NewVal, DstSema);
}
APSInt OtherVal = Other.getValue();
bool ThisSigned = Val.isSigned();
bool OtherSigned = OtherVal.isSigned();
- unsigned OtherScale = Other.getScale();
- unsigned OtherWidth = OtherVal.getBitWidth();
-
- unsigned CommonWidth = std::max(Val.getBitWidth(), OtherWidth);
- // Prevent overflow in the event the widths are the same but the scales differ
- CommonWidth += getScale() >= OtherScale ? getScale() - OtherScale
- : OtherScale - getScale();
+ int CommonLsb = std::min(getLsbWeight(), Other.getLsbWeight());
+ int CommonMsb = std::max(getMsbWeight(), Other.getMsbWeight());
+ unsigned CommonWidth = CommonMsb - CommonLsb + 1;
ThisVal = ThisVal.extOrTrunc(CommonWidth);
OtherVal = OtherVal.extOrTrunc(CommonWidth);
- unsigned CommonScale = std::max(getScale(), OtherScale);
- ThisVal = ThisVal.shl(CommonScale - getScale());
- OtherVal = OtherVal.shl(CommonScale - OtherScale);
+ ThisVal = ThisVal.shl(getLsbWeight() - CommonLsb);
+ OtherVal = OtherVal.shl(Other.getLsbWeight() - CommonLsb);
if (ThisSigned && OtherSigned) {
if (ThisVal.sgt(OtherVal))
FixedPointSemantics FixedPointSemantics::getCommonSemantics(
const FixedPointSemantics &Other) const {
- unsigned CommonScale = std::max(getScale(), Other.getScale());
- unsigned CommonWidth =
- std::max(getIntegralBits(), Other.getIntegralBits()) + CommonScale;
+ int CommonLsb = std::min(getLsbWeight(), Other.getLsbWeight());
+ int CommonMSb = std::max(getMsbWeight() - hasSignOrPaddingBit(),
+ Other.getMsbWeight() - Other.hasSignOrPaddingBit());
+ unsigned CommonWidth = CommonMSb - CommonLsb + 1;
bool ResultIsSigned = isSigned() || Other.isSigned();
bool ResultIsSaturated = isSaturated() || Other.isSaturated();
if (ResultIsSigned || ResultHasUnsignedPadding)
CommonWidth++;
- return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned,
+ return FixedPointSemantics(CommonWidth, Lsb{CommonLsb}, ResultIsSigned,
ResultIsSaturated, ResultHasUnsignedPadding);
}
APSInt Result;
if (CommonFXSema.isSigned())
Result = ThisVal.smul_ov(OtherVal, Overflowed)
- .ashr(CommonFXSema.getScale());
+ .relativeAShl(CommonFXSema.getLsbWeight());
else
Result = ThisVal.umul_ov(OtherVal, Overflowed)
- .lshr(CommonFXSema.getScale());
+ .relativeLShl(CommonFXSema.getLsbWeight());
assert(!Overflowed && "Full multiplication cannot overflow!");
Result.setIsSigned(CommonFXSema.isSigned());
bool Overflowed = false;
// Widen the LHS and RHS so we can perform a full division.
- unsigned Wide = CommonFXSema.getWidth() * 2;
+ // Also make sure that there will be enough space for the shift below to not
+ // overflow
+ unsigned Wide =
+ CommonFXSema.getWidth() * 2 + std::max(-CommonFXSema.getMsbWeight(), 0);
if (CommonFXSema.isSigned()) {
ThisVal = ThisVal.sext(Wide);
OtherVal = OtherVal.sext(Wide);
// Upscale to compensate for the loss of precision from division, and
// perform the full division.
- ThisVal = ThisVal.shl(CommonFXSema.getScale());
+ if (CommonFXSema.getLsbWeight() < 0)
+ ThisVal = ThisVal.shl(-CommonFXSema.getLsbWeight());
+ else if (CommonFXSema.getLsbWeight() > 0)
+ OtherVal = OtherVal.shl(CommonFXSema.getLsbWeight());
APSInt Result;
if (CommonFXSema.isSigned()) {
APInt Rem;
void APFixedPoint::toString(SmallVectorImpl<char> &Str) const {
APSInt Val = getValue();
- unsigned Scale = getScale();
+ int Lsb = getLsbWeight();
+ int OrigWidth = getWidth();
+
+ if (Lsb >= 0) {
+ APSInt IntPart = Val;
+ IntPart = IntPart.extend(IntPart.getBitWidth() + Lsb);
+ IntPart <<= Lsb;
+ IntPart.toString(Str, /*Radix=*/10);
+ Str.push_back('.');
+ Str.push_back('0');
+ return;
+ }
- if (Val.isSigned() && Val.isNegative() && Val != -Val) {
+ if (Val.isSigned() && Val.isNegative()) {
Val = -Val;
+ Val.setIsUnsigned(true);
Str.push_back('-');
}
- APSInt IntPart = Val >> Scale;
+ int Scale = -getLsbWeight();
+ APSInt IntPart = (OrigWidth > Scale) ? (Val >> Scale) : APSInt::get(0);
// Add 4 digits to hold the value after multiplying 10 (the radix)
- unsigned Width = Val.getBitWidth() + 4;
+ unsigned Width = std::max(OrigWidth, Scale) + 4;
APInt FractPart = Val.zextOrTrunc(Scale).zext(Width);
APInt FractPartMask = APInt::getAllOnes(Scale).zext(Width);
APInt RadixInt = APInt(Width, 10);
} while (FractPart != 0);
}
+void APFixedPoint::print(raw_ostream &OS) const {
+ OS << "APFixedPoint(" << toString() << ", {";
+ Sema.print(OS);
+ OS << "})";
+}
+LLVM_DUMP_METHOD void APFixedPoint::dump() const { print(llvm::errs()); }
+
APFixedPoint APFixedPoint::negate(bool *Overflow) const {
if (!isSaturated()) {
if (Overflow)
// Scale down the integer value in the float to match the correct scaling
// factor.
- APFloat ScaleFactor(std::pow(2, -(int)Sema.getScale()));
+ APFloat ScaleFactor(std::pow(2, Sema.getLsbWeight()));
bool Ignored;
ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
Flt.multiply(ScaleFactor, LosslessRM);
// the integer range instead. Rounding mode is irrelevant here.
// It is fine if this overflows to infinity even for saturating types,
// since we will use floating point comparisons to check for saturation.
- APFloat ScaleFactor(std::pow(2, DstFXSema.getScale()));
+ APFloat ScaleFactor(std::pow(2, -DstFXSema.getLsbWeight()));
ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
Val.multiply(ScaleFactor, LosslessRM);
// we risk checking for overflow with a value that is outside the
// representable range of the fixed-point semantic even though no overflow
// would occur had we rounded first.
- ScaleFactor = APFloat(std::pow(2, -(int)DstFXSema.getScale()));
+ ScaleFactor = APFloat(std::pow(2, DstFXSema.getLsbWeight()));
ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
Val.roundToIntegral(RM);
Val.multiply(ScaleFactor, LosslessRM);
/*hasUnsignedPadding=*/true);
}
+FixedPointSemantics getU8Neg10() {
+ return FixedPointSemantics(/*width=*/8, /*lsb=*/FixedPointSemantics::Lsb{-10},
+ /*isSigned=*/false,
+ /*isSaturated=*/false,
+ /*hasUnsignedPadding=*/false);
+}
+
+FixedPointSemantics getS16Neg18() {
+ return FixedPointSemantics(/*width=*/16,
+ /*lsb=*/FixedPointSemantics::Lsb{-18},
+ /*isSigned=*/true,
+ /*isSaturated=*/false,
+ /*hasUnsignedPadding=*/false);
+}
+
+FixedPointSemantics getU8Pos4() {
+ return FixedPointSemantics(/*width=*/8, /*lsb=*/FixedPointSemantics::Lsb{4},
+ /*isSigned=*/false,
+ /*isSaturated=*/false,
+ /*hasUnsignedPadding=*/false);
+}
+
+FixedPointSemantics getS32Pos2() {
+ return FixedPointSemantics(/*width=*/32, /*lsb=*/FixedPointSemantics::Lsb{2},
+ /*isSigned=*/true,
+ /*isSaturated=*/false,
+ /*hasUnsignedPadding=*/false);
+}
+
void CheckUnpaddedMax(const FixedPointSemantics &Sema) {
ASSERT_EQ(APFixedPoint::getMax(Sema).getValue(),
APSInt::getMaxValue(Sema.getWidth(), !Sema.isSigned()));
CheckUnpaddedMax(getUSFractSema());
CheckUnpaddedMax(getUFractSema());
CheckUnpaddedMax(getULFractSema());
+ CheckUnpaddedMax(getU8Neg10());
+ CheckUnpaddedMax(getS16Neg18());
+ CheckUnpaddedMax(getU8Pos4());
+ CheckUnpaddedMax(getS32Pos2());
CheckPaddedMax(getPadUSAccumSema());
CheckPaddedMax(getPadUAccumSema());
CheckMin(getUSFractSema());
CheckMin(getUFractSema());
CheckMin(getULFractSema());
+ CheckMin(getU8Neg10());
+ CheckMin(getS16Neg18());
+ CheckMin(getU8Pos4());
+ CheckMin(getS32Pos2());
CheckMin(getPadUSAccumSema());
CheckMin(getPadUAccumSema());
CheckMin(getPadULFractSema());
}
+int64_t relativeShr(int64_t Int, int64_t Shift) {
+ return (Shift > 0) ? Int >> Shift : Int << -Shift;
+}
+
void CheckIntPart(const FixedPointSemantics &Sema, int64_t IntPart) {
- unsigned Scale = Sema.getScale();
+ int64_t FullFactPart =
+ (Sema.getLsbWeight() > 0) ? 0 : (1ULL << (-Sema.getLsbWeight() - 1));
// Value with a fraction
- APFixedPoint ValWithFract(APInt(Sema.getWidth(),
- (IntPart << Scale) + (1ULL << (Scale - 1)),
- Sema.isSigned()),
- Sema);
+ APFixedPoint ValWithFract(
+ APInt(Sema.getWidth(),
+ relativeShr(IntPart, Sema.getLsbWeight()) + FullFactPart,
+ Sema.isSigned()),
+ Sema);
ASSERT_EQ(ValWithFract.getIntPart(), IntPart);
// Just fraction
- APFixedPoint JustFract(
- APInt(Sema.getWidth(), (1ULL << (Scale - 1)), Sema.isSigned()), Sema);
+ APFixedPoint JustFract(APInt(Sema.getWidth(), FullFactPart, Sema.isSigned()),
+ Sema);
ASSERT_EQ(JustFract.getIntPart(), 0);
// Whole number
- APFixedPoint WholeNum(
- APInt(Sema.getWidth(), (IntPart << Scale), Sema.isSigned()), Sema);
+ APFixedPoint WholeNum(APInt(Sema.getWidth(),
+ relativeShr(IntPart, Sema.getLsbWeight()),
+ Sema.isSigned()),
+ Sema);
ASSERT_EQ(WholeNum.getIntPart(), IntPart);
// Negative
if (Sema.isSigned()) {
- APFixedPoint Negative(
- APInt(Sema.getWidth(), (IntPart << Scale), Sema.isSigned()), Sema);
+ APFixedPoint Negative(APInt(Sema.getWidth(),
+ relativeShr(IntPart, Sema.getLsbWeight()),
+ Sema.isSigned()),
+ Sema);
ASSERT_EQ(Negative.getIntPart(), IntPart);
}
}
CheckIntPart(getUSAccumSema(), 2);
CheckIntPart(getUAccumSema(), 2);
CheckIntPart(getULAccumSema(), 2);
+ CheckIntPart(getU8Pos4(), 32);
+ CheckIntPart(getS32Pos2(), 32);
// Zero
CheckIntPart(getSAccumSema(), 0);
CheckIntPart(getUFractSema(), 0);
CheckIntPart(getULFractSema(), 0);
+ CheckIntPart(getS16Neg18(), 0);
+ CheckIntPart(getU8Neg10(), 0);
+ CheckIntPart(getU8Pos4(), 0);
+ CheckIntPart(getS32Pos2(), 0);
+
// Min
CheckIntPartMin(getSAccumSema(), -256);
CheckIntPartMin(getAccumSema(), -65536);
CheckIntPartMin(getFractSema(), -1);
CheckIntPartMin(getLFractSema(), -1);
+ CheckIntPartMin(getS32Pos2(), -8589934592);
+
// Max
CheckIntPartMax(getSAccumSema(), 255);
CheckIntPartMax(getAccumSema(), 65535);
CheckIntPartMax(getUAccumSema(), 65535);
CheckIntPartMax(getULAccumSema(), 4294967295);
+ CheckIntPartMax(getU8Pos4(), 255 << 4);
+ CheckIntPartMax(getS32Pos2(), 2147483647ull << 2);
+
CheckIntPartMax(getSFractSema(), 0);
CheckIntPartMax(getFractSema(), 0);
CheckIntPartMax(getLFractSema(), 0);
APFixedPoint(5368709120, getLAccumSema()));
ASSERT_EQ(APFixedPoint(0, getSAccumSema()), APFixedPoint(0, getLAccumSema()));
+ ASSERT_EQ(APFixedPoint(0, getS16Neg18()), APFixedPoint(0, getU8Neg10()));
+ ASSERT_EQ(APFixedPoint(256, getS16Neg18()), APFixedPoint(1, getU8Neg10()));
+ ASSERT_EQ(APFixedPoint(32512, getS16Neg18()),
+ APFixedPoint(127, getU8Neg10()));
+ ASSERT_EQ(APFixedPoint(4, getS32Pos2()), APFixedPoint(1, getU8Pos4()));
+ ASSERT_EQ(APFixedPoint(1020, getS32Pos2()), APFixedPoint(255, getU8Pos4()));
+
// Across types (0.5)
ASSERT_EQ(APFixedPoint(64, getSAccumSema()),
APFixedPoint(64, getSFractSema()));
ASSERT_LT(APFixedPoint(0, getUSAccumSema()), APFixedPoint(1, getAccumSema()));
ASSERT_LT(APFixedPoint(0, getUSAccumSema()),
APFixedPoint(1, getUAccumSema()));
+ ASSERT_LT(APFixedPoint(65280, getS16Neg18()),
+ APFixedPoint(255, getU8Neg10()));
// Greater than
ASSERT_GT(APFixedPoint(0, getAccumSema()), APFixedPoint(-1, getSAccumSema()));
if (IsNegative)
ScaledVal = -ScaledVal;
- if (Dst.getScale() > Src.getScale()) {
- ScaledVal <<= (Dst.getScale() - Src.getScale());
+ if (Dst.getLsbWeight() < Src.getLsbWeight()) {
+ ScaledVal <<= (Src.getLsbWeight() - Dst.getLsbWeight());
} else {
- ScaledVal >>= (Src.getScale() - Dst.getScale());
+ ScaledVal >>= (Dst.getLsbWeight() - Src.getLsbWeight());
}
if (IsNegative)
ASSERT_EQ(Ovf, false);
}
+TEST(FixedPoint, toString) {
+ ASSERT_EQ(APFixedPoint::getMax(getS16Neg18()).toString(),
+ "0.124996185302734375");
+ ASSERT_EQ(APFixedPoint::getMin(getS16Neg18())
+ .add(APFixedPoint(1, getS16Neg18()))
+ .toString(),
+ "-0.124996185302734375");
+ ASSERT_EQ(APFixedPoint::getMin(getS16Neg18()).toString(), "-0.125");
+ ASSERT_EQ(APFixedPoint::getMax(getU8Neg10()).toString(), "0.2490234375");
+ ASSERT_EQ(APFixedPoint::getMin(getU8Neg10()).toString(), "0.0");
+ ASSERT_EQ(APFixedPoint::getMax(getS32Pos2()).toString(), "8589934588.0");
+ ASSERT_EQ(APFixedPoint::getMin(getS32Pos2())
+ .add(APFixedPoint(1, getS32Pos2()))
+ .toString(),
+ "-8589934588.0");
+ ASSERT_EQ(APFixedPoint::getMin(getS32Pos2()).toString(), "-8589934592.0");
+ ASSERT_EQ(APFixedPoint::getMax(getU8Pos4()).toString(), "4080.0");
+ ASSERT_EQ(APFixedPoint::getMin(getU8Pos4()).toString(), "0.0");
+}
+
TEST(FixedPoint, FloatToFixed) {
APFloat Val(0.0f);
CheckFloatToFixedConversion(Val, getUFractSema(), 3ULL << 14);
CheckFloatToFixedConversion(Val, getULFractSema(), 3ULL << 30);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), 0);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), 0);
+
// Simple negative exact fraction
Val = APFloat(-0.75f);
CheckFloatToFixedConversion(Val, getSAccumSema(), -3ULL << 5);
CheckFloatToFixedConversion(Val, getUFractSema(), MinSat);
CheckFloatToFixedConversion(Val, getULFractSema(), MinSat);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), MinSat);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), 0);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), MinSat);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), 0);
+
// Highly precise fraction
Val = APFloat(0.999999940395355224609375f);
CheckFloatToFixedConversion(Val, getSAccumSema(), 0x7FULL);
CheckFloatToFixedConversion(Val, getUFractSema(), 0xFFFFULL);
CheckFloatToFixedConversion(Val, getULFractSema(), 0xFFFFFFULL << 8);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), 0);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), 0);
+
// Integral and fraction
Val = APFloat(17.99609375f);
CheckFloatToFixedConversion(Val, getSAccumSema(), 0x11FFULL >> 1);
CheckFloatToFixedConversion(Val, getUFractSema(), MaxSat);
CheckFloatToFixedConversion(Val, getULFractSema(), MaxSat);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), 1);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), 1 << 2);
+
// Negative integral and fraction
Val = APFloat(-17.99609375f);
CheckFloatToFixedConversion(Val, getSAccumSema(), -0x11FELL >> 1);
CheckFloatToFixedConversion(Val, getUFractSema(), MinSat);
CheckFloatToFixedConversion(Val, getULFractSema(), MinSat);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), MinSat);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), MinSat);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), MinSat);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), -4);
+
// Very large value
Val = APFloat(1.0e38f);
CheckFloatToFixedConversion(Val, getSAccumSema(), MaxSat);
CheckFloatToFixedConversion(Val, getUFractSema(), MaxSat);
CheckFloatToFixedConversion(Val, getULFractSema(), MaxSat);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), MaxSat);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), MaxSat);
+
// Very small value
Val = APFloat(1.0e-38f);
CheckFloatToFixedConversion(Val, getSAccumSema(), 0);
CheckFloatToFixedConversion(Val, getUFractSema(), 0);
CheckFloatToFixedConversion(Val, getULFractSema(), 0);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), 0);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), 0);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), 0);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), 0);
+
// Half conversion
Val = APFloat(0.99951171875f);
bool Ignored;
CheckFloatToFixedConversion(Val, getUSFractSema(), 0xFFULL);
CheckFloatToFixedConversion(Val, getUFractSema(), 0xFFEULL << 4);
CheckFloatToFixedConversion(Val, getULFractSema(), 0xFFEULL << 20);
+
+ CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), 0);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), 0);
+
+ Val = APFloat(0.124996185302734375);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), 0x7f);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), 0);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), 0x7fff);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), 0);
+
+ Val = APFloat(-0.124996185302734375);
+ CheckFloatToFixedConversion(Val, getU8Neg10(), MinSat);
+ CheckFloatToFixedConversion(Val, getU8Pos4(), 0);
+ CheckFloatToFixedConversion(Val, getS16Neg18(), -0x7fff);
+ CheckFloatToFixedConversion(Val, getS32Pos2(), 0);
}
void CheckFixedToFloatConversion(int64_t Val, const FixedPointSemantics &Sema,
CheckFixedToFloatConversion(Val, getULFractSema(),
0.00000000023283064365386962890625f);
+ CheckFixedToFloatConversion(Val, getU8Neg10(), 0.0009765625f);
+ CheckFixedToFloatConversion(Val, getU8Pos4(), 16.0f);
+ CheckFixedToFloatConversion(Val, getS16Neg18(), 0.000003814697265625f);
+ CheckFixedToFloatConversion(Val, getS32Pos2(), 4.0f);
+
Val = 0x7FULL;
CheckFixedToFloatConversion(Val, getSAccumSema(), 0.9921875f);
CheckFixedToFloatConversion(Val, getFractSema(), 0.003875732421875f);
CheckFixedToFloatConversion(Val, getULFractSema(),
0.00000002956949174404144287109375f);
+ CheckFixedToFloatConversion(Val, getU8Neg10(), 0.1240234375f);
+ CheckFixedToFloatConversion(Val, getU8Pos4(), 2032.0f);
+ CheckFixedToFloatConversion(Val, getS16Neg18(), 0.000484466552734375f);
+ CheckFixedToFloatConversion(Val, getS32Pos2(), 508.0f);
+
Val = -0x1ULL;
CheckFixedToFloatConversion(Val, getSAccumSema(), -0.0078125f);
CheckFixedToFloatConversion(Val, getFractSema(), -0.000030517578125f);
CheckFixedToFloatConversion(Val, getLFractSema(),
-0.0000000004656612873077392578125f);
+ CheckFixedToFloatConversion(Val, getU8Neg10(), 0.249023437f);
+ CheckFixedToFloatConversion(Val, getU8Pos4(), 4080.0f);
+ CheckFixedToFloatConversion(Val, getS16Neg18(), -0.000003814697265625f);
+ CheckFixedToFloatConversion(Val, getS32Pos2(), -4.0f);
CheckFixedToFloatConversion(-0x80ULL, getSAccumSema(), -1.0f);
CheckFixedToFloatConversion(-0x8000ULL, getFractSema(), -1.0f);
CheckFixedToFloatConversion(Val, getULFractSema(),
0.0000006542541086673736572265625f);
+ CheckFixedToFloatConversion(Val, getS16Neg18(), 0.01071929931640625f);
+ CheckFixedToFloatConversion(Val, getS32Pos2(), 11240.0f);
+
Val = -0xAFAULL;
CheckFixedToFloatConversion(Val, getSAccumSema(), -21.953125f);
CheckFixedToFloatConversion(Val, getFractSema(), -0.08575439453125f);
CheckFixedToFloatConversion(Val, getLFractSema(),
-0.000001308508217334747314453125f);
+ CheckFixedToFloatConversion(Val, getS16Neg18(), -0.01071929931640625f);
+ CheckFixedToFloatConversion(Val, getS32Pos2(), -11240.0f);
+
Val = 0x40000080ULL;
CheckFixedToFloatConversion(Val, getAccumSema(), 32768.00390625f);
CheckFixedToFloatConversion(Val, getLFractSema(),
CheckFixedToFloatConversion(Val, getULFractSema(),
0.2500000298023223876953125f);
+ CheckFixedToFloatConversion(Val, getS32Pos2(), 4294967808.0f);
+
Val = 0x40000040ULL;
CheckFixedToFloatConversion(Val, getAccumSema(), 32768.0f);
CheckFixedToFloatConversion(Val, getLFractSema(), 0.5f);
CheckFixedToFloatConversion(Val, getUAccumSema(), 16384.0f);
CheckFixedToFloatConversion(Val, getULFractSema(), 0.25f);
+ CheckFixedToFloatConversion(Val, getS32Pos2(), 4294967552.0f);
+
Val = 0x7FF0ULL;
CheckFixedToHalfConversion(Val, getAccumSema(), 0.99951171875f);
CheckFixedToHalfConversion(Val, getLFractSema(), 0.000015251338481903076171875f);
CheckFixedToHalfConversion(Val, getUAccumSema(), 0.499755859375f);
CheckFixedToHalfConversion(Val, getULFractSema(), 0.0000076256692409515380859375f);
+
+ CheckFixedToFloatConversion(Val, getS32Pos2(), 131008.0f);
+}
+
+void CheckAdd(const APFixedPoint &Lhs, const APFixedPoint &Rhs,
+ const APFixedPoint &Res) {
+ bool Overflow = false;
+ APFixedPoint Result = Lhs.add(Rhs, &Overflow);
+ ASSERT_FALSE(Overflow);
+ ASSERT_EQ(Result.getSemantics(), Res.getSemantics());
+ ASSERT_EQ(Result, Res);
+}
+
+void CheckAddOverflow(const APFixedPoint &Lhs, const APFixedPoint &Rhs) {
+ bool Overflow = false;
+ APFixedPoint Result = Lhs.add(Rhs, &Overflow);
+ ASSERT_TRUE(Overflow);
+}
+
+TEST(FixedPoint, add) {
+ CheckAdd(APFixedPoint(1, getS32Pos2()), APFixedPoint(1, getS32Pos2()),
+ APFixedPoint(2, getS32Pos2()));
+ CheckAdd(APFixedPoint(1, getS16Neg18()), APFixedPoint(1, getS16Neg18()),
+ APFixedPoint(2, getS16Neg18()));
+ CheckAdd(APFixedPoint(1, getU8Neg10()), APFixedPoint(1, getU8Neg10()),
+ APFixedPoint(2, getU8Neg10()));
+ CheckAdd(APFixedPoint(1, getU8Pos4()), APFixedPoint(1, getU8Pos4()),
+ APFixedPoint(2, getU8Pos4()));
+
+ CheckAdd(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS32Pos2()),
+ APFixedPoint(12, getS32Pos2()));
+ CheckAdd(APFixedPoint(11, getS16Neg18()), APFixedPoint(1, getS16Neg18()),
+ APFixedPoint(12, getS16Neg18()));
+ CheckAdd(APFixedPoint(11, getU8Neg10()), APFixedPoint(1, getU8Neg10()),
+ APFixedPoint(12, getU8Neg10()));
+ CheckAdd(APFixedPoint(11, getU8Pos4()), APFixedPoint(1, getU8Pos4()),
+ APFixedPoint(12, getU8Pos4()));
+
+ CheckAdd(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS16Neg18()),
+ APFixedPoint(11534337,
+ FixedPointSemantics(52, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckAdd(
+ APFixedPoint(11, getU8Neg10()), APFixedPoint(-9472, getS16Neg18()),
+ APFixedPoint(-6656, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckAddOverflow(
+ APFixedPoint::getMax(getU8Neg10()), APFixedPoint::getMax(getS16Neg18()));
+ CheckAdd(APFixedPoint::getMin(getU8Neg10()),
+ APFixedPoint::getMin(getS16Neg18()),
+ APFixedPoint::getMin(getS16Neg18())
+ .convert(FixedPointSemantics(17, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckAddOverflow(APFixedPoint::getMin(getS32Pos2()),
+ APFixedPoint::getMin(getS16Neg18()));
+}
+
+void CheckMul(const APFixedPoint &Lhs, const APFixedPoint &Rhs,
+ const APFixedPoint &Res) {
+ bool Overflow = false;
+ APFixedPoint Result = Lhs.mul(Rhs, &Overflow);
+ ASSERT_FALSE(Overflow);
+ ASSERT_EQ(Result.getSemantics(), Res.getSemantics());
+ ASSERT_EQ(Result, Res);
+}
+
+void CheckMulOverflow(const APFixedPoint &Lhs, const APFixedPoint &Rhs) {
+ bool Overflow = false;
+ APFixedPoint Result = Lhs.mul(Rhs, &Overflow);
+ ASSERT_TRUE(Overflow);
+}
+
+TEST(FixedPoint, mul) {
+ CheckMul(APFixedPoint(1, getS32Pos2()), APFixedPoint(1, getS32Pos2()),
+ APFixedPoint(4, getS32Pos2()));
+ CheckMul(APFixedPoint(1, getS16Neg18()), APFixedPoint(1, getS16Neg18()),
+ APFixedPoint(0, getS16Neg18()));
+ CheckMul(APFixedPoint(1, getU8Neg10()), APFixedPoint(1, getU8Neg10()),
+ APFixedPoint(0, getU8Neg10()));
+ CheckMul(APFixedPoint(1, getU8Pos4()), APFixedPoint(1, getU8Pos4()),
+ APFixedPoint(16, getU8Pos4()));
+
+ CheckMul(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS32Pos2()),
+ APFixedPoint(44, getS32Pos2()));
+ CheckMul(APFixedPoint(11, getS16Neg18()), APFixedPoint(1, getS16Neg18()),
+ APFixedPoint(0, getS16Neg18()));
+ CheckMul(APFixedPoint(11, getU8Neg10()), APFixedPoint(1, getU8Neg10()),
+ APFixedPoint(0, getU8Neg10()));
+ CheckMul(APFixedPoint(11, getU8Pos4()), APFixedPoint(1, getU8Pos4()),
+ APFixedPoint(176, getU8Pos4()));
+
+ CheckMul(APFixedPoint(512, getS16Neg18()), APFixedPoint(512, getS16Neg18()),
+ APFixedPoint(1, getS16Neg18()));
+ CheckMul(APFixedPoint(32, getU8Neg10()), APFixedPoint(32, getU8Neg10()),
+ APFixedPoint(1, getU8Neg10()));
+
+ CheckMul(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS16Neg18()),
+ APFixedPoint(44,
+ FixedPointSemantics(52, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckMul(
+ APFixedPoint(11, getU8Neg10()), APFixedPoint(-9472, getS16Neg18()),
+ APFixedPoint(-102, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckMul(
+ APFixedPoint::getMax(getU8Neg10()), APFixedPoint::getMax(getS16Neg18()),
+ APFixedPoint(8159, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckMul(
+ APFixedPoint::getMin(getU8Neg10()), APFixedPoint::getMin(getS16Neg18()),
+ APFixedPoint(0, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckMul(APFixedPoint::getMin(getS32Pos2()),
+ APFixedPoint::getMin(getS16Neg18()),
+ APFixedPoint(281474976710656,
+ FixedPointSemantics(52, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckMulOverflow(APFixedPoint::getMax(getS32Pos2()), APFixedPoint::getMax(getU8Pos4()));
+ CheckMulOverflow(APFixedPoint::getMin(getS32Pos2()), APFixedPoint::getMax(getU8Pos4()));
+}
+
+void CheckDiv(const APFixedPoint &Lhs, const APFixedPoint &Rhs,
+ const APFixedPoint &Expected) {
+ bool Overflow = false;
+ APFixedPoint Result = Lhs.div(Rhs, &Overflow);
+ ASSERT_FALSE(Overflow);
+ ASSERT_EQ(Result.getSemantics(), Expected.getSemantics());
+ ASSERT_EQ(Result, Expected);
+}
+
+void CheckDivOverflow(const APFixedPoint &Lhs, const APFixedPoint &Rhs) {
+ bool Overflow = false;
+ APFixedPoint Result = Lhs.div(Rhs, &Overflow);
+ ASSERT_TRUE(Overflow);
+}
+
+TEST(FixedPoint, div) {
+ CheckDiv(APFixedPoint(1, getS32Pos2()), APFixedPoint(1, getS32Pos2()),
+ APFixedPoint(0, getS32Pos2()));
+ CheckDivOverflow(APFixedPoint(1, getS16Neg18()), APFixedPoint(1, getS16Neg18()));
+ CheckDivOverflow(APFixedPoint(1, getU8Neg10()), APFixedPoint(1, getU8Neg10()));
+ CheckDiv(APFixedPoint(1, getU8Pos4()), APFixedPoint(1, getU8Pos4()),
+ APFixedPoint(0, getU8Pos4()));
+
+ CheckDiv(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS32Pos2()),
+ APFixedPoint(2, getS32Pos2()));
+ CheckDiv(APFixedPoint(11, getU8Pos4()), APFixedPoint(1, getU8Pos4()),
+ APFixedPoint(0, getU8Pos4()));
+
+ CheckDiv(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS16Neg18()),
+ APFixedPoint(3023656976384,
+ FixedPointSemantics(52, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckDiv(APFixedPoint(11, getU8Neg10()), APFixedPoint(-11264, getS16Neg18()),
+ APFixedPoint::getMin(FixedPointSemantics(
+ 17, FixedPointSemantics::Lsb{-18}, true, false, false)));
+ CheckDiv(APFixedPoint(11, getU8Neg10()), APFixedPoint(11265, getS16Neg18()),
+ APFixedPoint(0xfffa,
+ FixedPointSemantics(17, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckDivOverflow(APFixedPoint(11, getU8Neg10()),
+ APFixedPoint(11264, getS16Neg18()));
+
+ CheckDivOverflow(APFixedPoint(11, getU8Neg10()),
+ APFixedPoint(-9472, getS16Neg18()));
+ CheckDivOverflow(APFixedPoint::getMax(getU8Neg10()),
+ APFixedPoint::getMax(getS16Neg18()));
+ CheckDiv(
+ APFixedPoint::getMin(getU8Neg10()), APFixedPoint::getMin(getS16Neg18()),
+ APFixedPoint(0, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
+ CheckDiv(
+ APFixedPoint(1, getU8Neg10()), APFixedPoint::getMin(getS16Neg18()),
+ APFixedPoint(-2048, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18},
+ true, false, false)));
}
} // namespace