@@ -5749,16 +5749,15 @@ int DoubleAPFloat::getExactLog2Abs() const {
57495749 return getFirst ().getExactLog2Abs ();
57505750}
57515751
5752- int ilogb (const DoubleAPFloat& Arg) {
5753- const APFloat& Hi = Arg.getFirst ();
5754- const APFloat& Lo = Arg.getSecond ();
5752+ int ilogb (const DoubleAPFloat & Arg) {
5753+ const APFloat & Hi = Arg.getFirst ();
5754+ const APFloat & Lo = Arg.getSecond ();
57555755 int IlogbResult = ilogb (Hi);
57565756 // Zero and non-finite values can delegate to ilogb(Hi).
57575757 if (Arg.getCategory () != fcNormal)
57585758 return IlogbResult;
57595759 // If Lo can't change the binade, we can delegate to ilogb(Hi).
5760- if (Lo.isZero () ||
5761- Hi.isNegative () == Lo.isNegative ())
5760+ if (Lo.isZero () || Hi.isNegative () == Lo.isNegative ())
57625761 return IlogbResult;
57635762 if (Hi.getExactLog2Abs () == INT_MIN)
57645763 return IlogbResult;
@@ -5777,10 +5776,101 @@ DoubleAPFloat scalbn(const DoubleAPFloat &Arg, int Exp,
57775776DoubleAPFloat frexp (const DoubleAPFloat &Arg, int &Exp,
57785777 APFloat::roundingMode RM) {
57795778 assert (Arg.Semantics == &semPPCDoubleDouble && " Unexpected Semantics" );
5780- APFloat First = frexp (Arg.Floats [0 ], Exp, RM);
5781- APFloat Second = Arg.Floats [1 ];
5782- if (Arg.getCategory () == APFloat::fcNormal)
5783- Second = scalbn (Second, -Exp, RM);
5779+
5780+ // Get the unbiased exponent e of the number, where |Arg| = m * 2^e for m in
5781+ // [1.0, 2.0).
5782+ Exp = ilogb (Arg);
5783+
5784+ // For NaNs, quiet any signaling NaN and return the result, as per standard
5785+ // practice.
5786+ if (Exp == APFloat::IEK_NaN) {
5787+ DoubleAPFloat Quiet{Arg};
5788+ Quiet.getFirst ().makeQuiet ();
5789+ return Quiet;
5790+ }
5791+
5792+ // For infinity, return it unchanged. The exponent remains IEK_Inf.
5793+ if (Exp == APFloat::IEK_Inf)
5794+ return Arg;
5795+
5796+ // For zero, the fraction is zero and the standard requires the exponent be 0.
5797+ if (Exp == APFloat::IEK_Zero) {
5798+ Exp = 0 ;
5799+ return Arg;
5800+ }
5801+
5802+ const APFloat &Hi = Arg.getFirst ();
5803+ const APFloat &Lo = Arg.getSecond ();
5804+
5805+ // frexp requires the fraction's absolute value to be in [0.5, 1.0).
5806+ // ilogb provides an exponent for an absolute value in [1.0, 2.0).
5807+ // Increment the exponent to ensure the fraction is in the correct range.
5808+ ++Exp;
5809+
5810+ const bool SignsDisagree = Hi.isNegative () != Lo.isNegative ();
5811+ APFloat Second = Lo;
5812+ if (Arg.getCategory () == APFloat::fcNormal && Lo.isFiniteNonZero ()) {
5813+ roundingMode LoRoundingMode;
5814+ // The interpretation of rmTowardZero depends on the sign of the combined
5815+ // Arg rather than the sign of the component.
5816+ if (RM == rmTowardZero)
5817+ LoRoundingMode = Arg.isNegative () ? rmTowardPositive : rmTowardNegative;
5818+ // For rmNearestTiesToAway, we face a similar problem. If signs disagree,
5819+ // Lo is a correction *toward* zero relative to Hi. Rounding Lo
5820+ // "away from zero" based on its own sign would move the value in the
5821+ // wrong direction. As a safe proxy, we use rmNearestTiesToEven, which is
5822+ // direction-agnostic. We only need to bother with this if Lo is scaled
5823+ // down.
5824+ else if (RM == rmNearestTiesToAway && SignsDisagree && Exp > 0 )
5825+ LoRoundingMode = rmNearestTiesToEven;
5826+ else
5827+ LoRoundingMode = RM;
5828+ Second = scalbn (Lo, -Exp, LoRoundingMode);
5829+ // The rmNearestTiesToEven proxy is correct most of the time, but it
5830+ // differs from rmNearestTiesToAway when the scaled value of Lo is an
5831+ // exact midpoint.
5832+ // NOTE: This is morally equivalent to roundTiesTowardZero.
5833+ if (RM == rmNearestTiesToAway && LoRoundingMode == rmNearestTiesToEven) {
5834+ // Re-scale the result back to check if rounding occurred.
5835+ const APFloat RecomposedLo = scalbn (Second, Exp, rmNearestTiesToEven);
5836+ if (RecomposedLo != Lo) {
5837+ // RoundingError tells us which direction we rounded:
5838+ // - RoundingError > 0: we rounded up.
5839+ // - RoundingError < 0: we down up.
5840+ const APFloat RoundingError = RecomposedLo - Lo;
5841+ // Determine if scalbn(Lo, -Exp) landed exactly on a midpoint.
5842+ // We do this by checking if the absolute rounding error is exactly
5843+ // half a ULP of the result.
5844+ const APFloat UlpOfSecond = harrisonUlp (Second);
5845+ const APFloat ScaledUlpOfSecond =
5846+ scalbn (UlpOfSecond, Exp - 1 , rmNearestTiesToEven);
5847+ const bool IsMidpoint = abs (RoundingError) == ScaledUlpOfSecond;
5848+ const bool RoundedLoAway =
5849+ Second.isNegative () == RoundingError.isNegative ();
5850+ // The sign of Hi and Lo disagree and we rounded Lo away: we must
5851+ // decrease the magnitude of Second to increase the magnitude
5852+ // First+Second.
5853+ if (IsMidpoint && RoundedLoAway)
5854+ Second.next (/* nextDown=*/ !Second.isNegative ());
5855+ }
5856+ }
5857+ // Handle a tricky edge case where Arg is slightly less than a power of two
5858+ // (e.g., Arg = 2^k - epsilon). In this situation:
5859+ // 1. Hi is 2^k, and Lo is a small negative value -epsilon.
5860+ // 2. ilogb(Arg) correctly returns k-1.
5861+ // 3. Our initial Exp becomes (k-1) + 1 = k.
5862+ // 4. Scaling Hi (2^k) by 2^-k would yield a magnitude of 1.0 and
5863+ // scaling Lo by 2^-k would yield zero. This would make the result 1.0
5864+ // which is an invalid fraction, as the required interval is [0.5, 1.0).
5865+ // We detect this specific case by checking if Hi is a power of two and if
5866+ // the scaled Lo underflowed to zero. The fix: Increment Exp to k+1. This
5867+ // adjusts the scale factor, causing Hi to be scaled to 0.5, which is a
5868+ // valid fraction.
5869+ if (Second.isZero () && SignsDisagree && Hi.getExactLog2Abs () != INT_MIN)
5870+ ++Exp;
5871+ }
5872+
5873+ APFloat First = scalbn (Hi, -Exp, RM);
57845874 return DoubleAPFloat (semPPCDoubleDouble, std::move (First), std::move (Second));
57855875}
57865876
0 commit comments