Skip to content

Commit cb0b240

Browse files
majnemergithub-actions[bot]
authored andcommitted
Automerge: [APFloat] Properly implement DoubleAPFloat::convertToSignExtendedInteger
Use DoubleAPFloat::roundToIntegral to get a pair of APFloat values which hold integral values. Then we sum the pair, taking care to make sure that we handle edge cases like (hi=2^128, lo=-1) and ensuring that they fit in an unsigned i128.
2 parents 0e83362 + 052c38b commit cb0b240

File tree

3 files changed

+540
-51
lines changed

3 files changed

+540
-51
lines changed

llvm/include/llvm/ADT/APFloat.h

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -609,29 +609,10 @@ class IEEEFloat final {
609609
/// return true.
610610
LLVM_ABI bool getExactInverse(APFloat *inv) const;
611611

612-
// If this is an exact power of two, return the exponent while ignoring the
613-
// sign bit. If it's not an exact power of 2, return INT_MIN
614612
LLVM_ABI LLVM_READONLY int getExactLog2Abs() const;
615613

616-
// If this is an exact power of two, return the exponent. If it's not an exact
617-
// power of 2, return INT_MIN
618-
LLVM_READONLY
619-
int getExactLog2() const {
620-
return isNegative() ? INT_MIN : getExactLog2Abs();
621-
}
622-
623-
/// Returns the exponent of the internal representation of the APFloat.
624-
///
625-
/// Because the radix of APFloat is 2, this is equivalent to floor(log2(x)).
626-
/// For special APFloat values, this returns special error codes:
627-
///
628-
/// NaN -> \c IEK_NaN
629-
/// 0 -> \c IEK_Zero
630-
/// Inf -> \c IEK_Inf
631-
///
632614
LLVM_ABI friend int ilogb(const IEEEFloat &Arg);
633615

634-
/// Returns: X * 2^Exp for integral exponents.
635616
LLVM_ABI friend IEEEFloat scalbn(IEEEFloat X, int Exp, roundingMode);
636617

637618
LLVM_ABI friend IEEEFloat frexp(const IEEEFloat &X, int &Exp, roundingMode);
@@ -806,7 +787,17 @@ class IEEEFloat final {
806787
};
807788

808789
LLVM_ABI hash_code hash_value(const IEEEFloat &Arg);
790+
/// Returns the exponent of the internal representation of the APFloat.
791+
///
792+
/// Because the radix of APFloat is 2, this is equivalent to floor(log2(x)).
793+
/// For special APFloat values, this returns special error codes:
794+
///
795+
/// NaN -> \c IEK_NaN
796+
/// 0 -> \c IEK_Zero
797+
/// Inf -> \c IEK_Inf
798+
///
809799
LLVM_ABI int ilogb(const IEEEFloat &Arg);
800+
/// Returns: X * 2^Exp for integral exponents.
810801
LLVM_ABI IEEEFloat scalbn(IEEEFloat X, int Exp, roundingMode);
811802
LLVM_ABI IEEEFloat frexp(const IEEEFloat &Val, int &Exp, roundingMode RM);
812803

@@ -824,6 +815,9 @@ class DoubleAPFloat final {
824815

825816
opStatus addWithSpecial(const DoubleAPFloat &LHS, const DoubleAPFloat &RHS,
826817
DoubleAPFloat &Out, roundingMode RM);
818+
opStatus convertToSignExtendedInteger(MutableArrayRef<integerPart> Input,
819+
unsigned int Width, bool IsSigned,
820+
roundingMode RM, bool *IsExact) const;
827821

828822
public:
829823
LLVM_ABI DoubleAPFloat(const fltSemantics &S);
@@ -904,9 +898,9 @@ class DoubleAPFloat final {
904898

905899
LLVM_ABI bool getExactInverse(APFloat *inv) const;
906900

907-
LLVM_ABI LLVM_READONLY int getExactLog2() const;
908901
LLVM_ABI LLVM_READONLY int getExactLog2Abs() const;
909902

903+
LLVM_ABI friend int ilogb(const DoubleAPFloat &X);
910904
LLVM_ABI friend DoubleAPFloat scalbn(const DoubleAPFloat &X, int Exp,
911905
roundingMode);
912906
LLVM_ABI friend DoubleAPFloat frexp(const DoubleAPFloat &X, int &Exp,
@@ -1345,12 +1339,23 @@ class APFloat : public APFloatBase {
13451339

13461340
LLVM_ABI opStatus convert(const fltSemantics &ToSemantics, roundingMode RM,
13471341
bool *losesInfo);
1342+
// Convert a floating point number to an integer according to the
1343+
// rounding mode. We provide deterministic values in case of an invalid
1344+
// operation exception, namely zero for NaNs and the minimal or maximal value
1345+
// respectively for underflow or overflow.
1346+
// The *IsExact output tells whether the result is exact, in the sense that
1347+
// converting it back to the original floating point type produces the
1348+
// original value. This is almost equivalent to result==opOK, except for
1349+
// negative zeroes.
13481350
opStatus convertToInteger(MutableArrayRef<integerPart> Input,
13491351
unsigned int Width, bool IsSigned, roundingMode RM,
13501352
bool *IsExact) const {
13511353
APFLOAT_DISPATCH_ON_SEMANTICS(
13521354
convertToInteger(Input, Width, IsSigned, RM, IsExact));
13531355
}
1356+
// Same as convertToInteger(integerPart*, ...), except the result is returned
1357+
// in an APSInt, whose initial bit-width and signed-ness are used to determine
1358+
// the precision of the conversion.
13541359
LLVM_ABI opStatus convertToInteger(APSInt &Result, roundingMode RM,
13551360
bool *IsExact) const;
13561361
opStatus convertFromAPInt(const APInt &Input, bool IsSigned,
@@ -1509,18 +1514,28 @@ class APFloat : public APFloatBase {
15091514
APFLOAT_DISPATCH_ON_SEMANTICS(getExactInverse(inv));
15101515
}
15111516

1517+
// If this is an exact power of two, return the exponent while ignoring the
1518+
// sign bit. If it's not an exact power of 2, return INT_MIN
15121519
LLVM_READONLY
15131520
int getExactLog2Abs() const {
15141521
APFLOAT_DISPATCH_ON_SEMANTICS(getExactLog2Abs());
15151522
}
15161523

1524+
// If this is an exact power of two, return the exponent. If it's not an exact
1525+
// power of 2, return INT_MIN
15171526
LLVM_READONLY
15181527
int getExactLog2() const {
1519-
APFLOAT_DISPATCH_ON_SEMANTICS(getExactLog2());
1528+
return isNegative() ? INT_MIN : getExactLog2Abs();
15201529
}
15211530

15221531
LLVM_ABI friend hash_code hash_value(const APFloat &Arg);
1523-
friend int ilogb(const APFloat &Arg) { return ilogb(Arg.getIEEE()); }
1532+
friend int ilogb(const APFloat &Arg) {
1533+
if (APFloat::usesLayout<detail::IEEEFloat>(Arg.getSemantics()))
1534+
return ilogb(Arg.getIEEE());
1535+
if (APFloat::usesLayout<detail::DoubleAPFloat>(Arg.getSemantics()))
1536+
return ilogb(Arg.getIEEE());
1537+
llvm_unreachable("Unexpected semantics");
1538+
}
15241539
friend APFloat scalbn(APFloat X, int Exp, roundingMode RM);
15251540
friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM);
15261541
friend IEEEFloat;

llvm/lib/Support/APFloat.cpp

Lines changed: 141 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5519,13 +5519,127 @@ APFloat::opStatus DoubleAPFloat::next(bool nextDown) {
55195519
return opOK;
55205520
}
55215521

5522+
APFloat::opStatus DoubleAPFloat::convertToSignExtendedInteger(
5523+
MutableArrayRef<integerPart> Input, unsigned int Width, bool IsSigned,
5524+
roundingMode RM, bool *IsExact) const {
5525+
assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics");
5526+
5527+
// If Hi is not finite, or Lo is zero, the value is entirely represented
5528+
// by Hi. Delegate to the simpler single-APFloat conversion.
5529+
if (!getFirst().isFiniteNonZero() || getSecond().isZero())
5530+
return getFirst().convertToInteger(Input, Width, IsSigned, RM, IsExact);
5531+
5532+
// First, round the full double-double value to an integral value. This
5533+
// simplifies the rest of the function, as we no longer need to consider
5534+
// fractional parts.
5535+
*IsExact = false;
5536+
DoubleAPFloat Integral = *this;
5537+
const opStatus RoundStatus = Integral.roundToIntegral(RM);
5538+
if (RoundStatus == opInvalidOp)
5539+
return RoundStatus;
5540+
const APFloat &IntegralHi = Integral.getFirst();
5541+
const APFloat &IntegralLo = Integral.getSecond();
5542+
5543+
// If rounding results in either component being zero, the sum is trivial.
5544+
// Delegate to the simpler single-APFloat conversion.
5545+
bool HiIsExact;
5546+
if (IntegralHi.isZero() || IntegralLo.isZero()) {
5547+
const opStatus HiStatus =
5548+
IntegralHi.convertToInteger(Input, Width, IsSigned, RM, &HiIsExact);
5549+
// The conversion from an integer-valued float to an APInt may fail if the
5550+
// result would be out of range. Regardless, taking this path is only
5551+
// possible if rounding occured during the initial `roundToIntegral`.
5552+
return HiStatus == opOK ? opInexact : HiStatus;
5553+
}
5554+
5555+
// A negative number cannot be represented by an unsigned integer.
5556+
// Since a double-double is canonical, if Hi is negative, the sum is negative.
5557+
if (!IsSigned && IntegralHi.isNegative())
5558+
return opInvalidOp;
5559+
5560+
// Handle the special boundary case where |Hi| is exactly the power of two
5561+
// that marks the edge of the integer's range (e.g., 2^63 for int64_t). In
5562+
// this situation, Hi itself won't fit, but the sum Hi + Lo might.
5563+
// `PositiveOverflowWidth` is the bit number for this boundary (N-1 for
5564+
// signed, N for unsigned).
5565+
bool LoIsExact;
5566+
const int HiExactLog2 = IntegralHi.getExactLog2Abs();
5567+
const unsigned PositiveOverflowWidth = IsSigned ? Width - 1 : Width;
5568+
if (HiExactLog2 >= 0 &&
5569+
static_cast<unsigned>(HiExactLog2) == PositiveOverflowWidth) {
5570+
// If Hi and Lo have the same sign, |Hi + Lo| > |Hi|, so the sum is
5571+
// guaranteed to overflow. E.g., for uint128_t, (2^128, 1) overflows.
5572+
if (IntegralHi.isNegative() == IntegralLo.isNegative())
5573+
return opInvalidOp;
5574+
5575+
// If the signs differ, the sum will fit. We can compute the result using
5576+
// properties of two's complement arithmetic without a wide intermediate
5577+
// integer. E.g., for uint128_t, (2^128, -1) should be 2^128 - 1.
5578+
[[maybe_unused]] opStatus LoStatus = IntegralLo.convertToInteger(
5579+
Input, Width, /*IsSigned=*/true, RM, &LoIsExact);
5580+
assert(LoStatus == opOK && "Unexpected failure");
5581+
5582+
// Adjust the bit pattern of Lo to account for Hi's value:
5583+
// - For unsigned (Hi=2^Width): `2^Width + Lo` in `Width`-bit
5584+
// arithmetic is equivalent to just `Lo`. The conversion of `Lo` above
5585+
// already produced the correct final bit pattern.
5586+
// - For signed (Hi=2^(Width-1)): The sum `2^(Width-1) + Lo` (where Lo<0)
5587+
// can be computed by taking the two's complement pattern for `Lo` and
5588+
// clearing the sign bit.
5589+
if (IsSigned && !IntegralHi.isNegative())
5590+
APInt::tcClearBit(Input.data(), PositiveOverflowWidth);
5591+
*IsExact = RoundStatus == opOK;
5592+
return RoundStatus;
5593+
}
5594+
5595+
// General case: Hi is not a power-of-two boundary, so we know it fits.
5596+
// Since we already rounded the full value, we now just need to convert the
5597+
// components to integers. The rounding mode should not matter.
5598+
[[maybe_unused]] opStatus HiStatus = IntegralHi.convertToInteger(
5599+
Input, Width, IsSigned, rmTowardZero, &HiIsExact);
5600+
assert(HiStatus == opOK && "Unexpected failure");
5601+
5602+
// Convert Lo into a temporary integer of the same width.
5603+
APSInt LoResult{Width, /*isUnsigned=*/!IsSigned};
5604+
[[maybe_unused]] opStatus LoStatus =
5605+
IntegralLo.convertToInteger(LoResult, rmTowardZero, &LoIsExact);
5606+
assert(LoStatus == opOK && "Unexpected failure");
5607+
5608+
// Add Lo to Hi. This addition is guaranteed not to overflow because of the
5609+
// double-double canonicalization rule (`|Lo| <= ulp(Hi)/2`). The only case
5610+
// where the sum could cross the integer type's boundary is when Hi is a
5611+
// power of two, which is handled by the special case block above.
5612+
APInt::tcAdd(Input.data(), LoResult.getRawData(), /*carry=*/0, Input.size());
5613+
5614+
*IsExact = RoundStatus == opOK;
5615+
return RoundStatus;
5616+
}
5617+
55225618
APFloat::opStatus
55235619
DoubleAPFloat::convertToInteger(MutableArrayRef<integerPart> Input,
55245620
unsigned int Width, bool IsSigned,
55255621
roundingMode RM, bool *IsExact) const {
5526-
assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics");
5527-
return APFloat(semPPCDoubleDoubleLegacy, bitcastToAPInt())
5528-
.convertToInteger(Input, Width, IsSigned, RM, IsExact);
5622+
opStatus FS =
5623+
convertToSignExtendedInteger(Input, Width, IsSigned, RM, IsExact);
5624+
5625+
if (FS == opInvalidOp) {
5626+
const unsigned DstPartsCount = partCountForBits(Width);
5627+
assert(DstPartsCount <= Parts.size() && "Integer too big");
5628+
5629+
unsigned Bits;
5630+
if (getCategory() == fcNaN)
5631+
Bits = 0;
5632+
else if (isNegative())
5633+
Bits = IsSigned;
5634+
else
5635+
Bits = Width - IsSigned;
5636+
5637+
tcSetLeastSignificantBits(Input.data(), DstPartsCount, Bits);
5638+
if (isNegative() && IsSigned)
5639+
APInt::tcShiftLeft(Input.data(), DstPartsCount, Width - 1);
5640+
}
5641+
5642+
return FS;
55295643
}
55305644

55315645
APFloat::opStatus DoubleAPFloat::convertFromAPInt(const APInt &Input,
@@ -5626,14 +5740,31 @@ bool DoubleAPFloat::getExactInverse(APFloat *inv) const {
56265740
return Ret;
56275741
}
56285742

5629-
int DoubleAPFloat::getExactLog2() const {
5630-
// TODO: Implement me
5631-
return INT_MIN;
5632-
}
5633-
56345743
int DoubleAPFloat::getExactLog2Abs() const {
5635-
// TODO: Implement me
5636-
return INT_MIN;
5744+
// In order for Hi + Lo to be a power of two, the following must be true:
5745+
// 1. Hi must be a power of two.
5746+
// 2. Lo must be zero.
5747+
if (getSecond().isNonZero())
5748+
return INT_MIN;
5749+
return getFirst().getExactLog2Abs();
5750+
}
5751+
5752+
int ilogb(const DoubleAPFloat& Arg) {
5753+
const APFloat& Hi = Arg.getFirst();
5754+
const APFloat& Lo = Arg.getSecond();
5755+
int IlogbResult = ilogb(Hi);
5756+
// Zero and non-finite values can delegate to ilogb(Hi).
5757+
if (Arg.getCategory() != fcNormal)
5758+
return IlogbResult;
5759+
// If Lo can't change the binade, we can delegate to ilogb(Hi).
5760+
if (Lo.isZero() ||
5761+
Hi.isNegative() == Lo.isNegative())
5762+
return IlogbResult;
5763+
if (Hi.getExactLog2Abs() == INT_MIN)
5764+
return IlogbResult;
5765+
// Numbers of the form 2^a - 2^b or -2^a + 2^b are almost powers of two but
5766+
// get nudged out of the binade by the low component.
5767+
return IlogbResult - 1;
56375768
}
56385769

56395770
DoubleAPFloat scalbn(const DoubleAPFloat &Arg, int Exp,
@@ -5749,10 +5880,6 @@ void APFloat::Profile(FoldingSetNodeID &NID) const {
57495880
NID.Add(bitcastToAPInt());
57505881
}
57515882

5752-
/* Same as convertToInteger(integerPart*, ...), except the result is returned in
5753-
an APSInt, whose initial bit-width and signed-ness are used to determine the
5754-
precision of the conversion.
5755-
*/
57565883
APFloat::opStatus APFloat::convertToInteger(APSInt &result,
57575884
roundingMode rounding_mode,
57585885
bool *isExact) const {

0 commit comments

Comments
 (0)