@@ -5749,16 +5749,15 @@ int DoubleAPFloat::getExactLog2Abs() const {
5749
5749
return getFirst ().getExactLog2Abs ();
5750
5750
}
5751
5751
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 ();
5755
5755
int IlogbResult = ilogb (Hi);
5756
5756
// Zero and non-finite values can delegate to ilogb(Hi).
5757
5757
if (Arg.getCategory () != fcNormal)
5758
5758
return IlogbResult;
5759
5759
// 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 ())
5762
5761
return IlogbResult;
5763
5762
if (Hi.getExactLog2Abs () == INT_MIN)
5764
5763
return IlogbResult;
@@ -5777,10 +5776,101 @@ DoubleAPFloat scalbn(const DoubleAPFloat &Arg, int Exp,
5777
5776
DoubleAPFloat frexp (const DoubleAPFloat &Arg, int &Exp,
5778
5777
APFloat::roundingMode RM) {
5779
5778
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);
5784
5874
return DoubleAPFloat (semPPCDoubleDouble, std::move (First), std::move (Second));
5785
5875
}
5786
5876
0 commit comments