1 //===- APFixedPoint.cpp - Fixed point constant handling ---------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// Defines the implementation for the fixed point number interface.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/ADT/APFixedPoint.h"
15 #include "llvm/ADT/APFloat.h"
21 void FixedPointSemantics::print(llvm::raw_ostream
&OS
) const {
22 OS
<< "width=" << getWidth() << ", ";
23 if (isValidLegacySema())
24 OS
<< "scale=" << getScale() << ", ";
25 OS
<< "msb=" << getMsbWeight() << ", ";
26 OS
<< "lsb=" << getLsbWeight() << ", ";
27 OS
<< "IsSigned=" << IsSigned
<< ", ";
28 OS
<< "HasUnsignedPadding=" << HasUnsignedPadding
<< ", ";
29 OS
<< "IsSaturated=" << IsSaturated
;
32 APFixedPoint
APFixedPoint::convert(const FixedPointSemantics
&DstSema
,
33 bool *Overflow
) const {
35 int RelativeUpscale
= getLsbWeight() - DstSema
.getLsbWeight();
39 if (RelativeUpscale
> 0)
40 NewVal
= NewVal
.extend(NewVal
.getBitWidth() + RelativeUpscale
);
41 NewVal
= NewVal
.relativeShl(RelativeUpscale
);
43 auto Mask
= APInt::getBitsSetFrom(
45 std::min(DstSema
.getIntegralBits() - DstSema
.getLsbWeight(),
46 NewVal
.getBitWidth()));
47 APInt
Masked(NewVal
& Mask
);
49 // Change in the bits above the sign
50 if (!(Masked
== Mask
|| Masked
== 0)) {
51 // Found overflow in the bits above the sign
52 if (DstSema
.isSaturated())
53 NewVal
= NewVal
.isNegative() ? Mask
: ~Mask
;
58 // If the dst semantics are unsigned, but our value is signed and negative, we
60 if (!DstSema
.isSigned() && NewVal
.isSigned() && NewVal
.isNegative()) {
61 // Found negative overflow for unsigned result
62 if (DstSema
.isSaturated())
68 NewVal
= NewVal
.extOrTrunc(DstSema
.getWidth());
69 NewVal
.setIsSigned(DstSema
.isSigned());
70 return APFixedPoint(NewVal
, DstSema
);
73 int APFixedPoint::compare(const APFixedPoint
&Other
) const {
74 APSInt ThisVal
= getValue();
75 APSInt OtherVal
= Other
.getValue();
76 bool ThisSigned
= Val
.isSigned();
77 bool OtherSigned
= OtherVal
.isSigned();
79 int CommonLsb
= std::min(getLsbWeight(), Other
.getLsbWeight());
80 int CommonMsb
= std::max(getMsbWeight(), Other
.getMsbWeight());
81 unsigned CommonWidth
= CommonMsb
- CommonLsb
+ 1;
83 ThisVal
= ThisVal
.extOrTrunc(CommonWidth
);
84 OtherVal
= OtherVal
.extOrTrunc(CommonWidth
);
86 ThisVal
= ThisVal
.shl(getLsbWeight() - CommonLsb
);
87 OtherVal
= OtherVal
.shl(Other
.getLsbWeight() - CommonLsb
);
89 if (ThisSigned
&& OtherSigned
) {
90 if (ThisVal
.sgt(OtherVal
))
92 else if (ThisVal
.slt(OtherVal
))
94 } else if (!ThisSigned
&& !OtherSigned
) {
95 if (ThisVal
.ugt(OtherVal
))
97 else if (ThisVal
.ult(OtherVal
))
99 } else if (ThisSigned
&& !OtherSigned
) {
100 if (ThisVal
.isSignBitSet())
102 else if (ThisVal
.ugt(OtherVal
))
104 else if (ThisVal
.ult(OtherVal
))
107 // !ThisSigned && OtherSigned
108 if (OtherVal
.isSignBitSet())
110 else if (ThisVal
.ugt(OtherVal
))
112 else if (ThisVal
.ult(OtherVal
))
119 APFixedPoint
APFixedPoint::getMax(const FixedPointSemantics
&Sema
) {
120 bool IsUnsigned
= !Sema
.isSigned();
121 auto Val
= APSInt::getMaxValue(Sema
.getWidth(), IsUnsigned
);
122 if (IsUnsigned
&& Sema
.hasUnsignedPadding())
124 return APFixedPoint(Val
, Sema
);
127 APFixedPoint
APFixedPoint::getMin(const FixedPointSemantics
&Sema
) {
128 auto Val
= APSInt::getMinValue(Sema
.getWidth(), !Sema
.isSigned());
129 return APFixedPoint(Val
, Sema
);
132 bool FixedPointSemantics::fitsInFloatSemantics(
133 const fltSemantics
&FloatSema
) const {
134 // A fixed point semantic fits in a floating point semantic if the maximum
135 // and minimum values as integers of the fixed point semantic can fit in the
136 // floating point semantic.
138 // If these values do not fit, then a floating point rescaling of the true
139 // maximum/minimum value will not fit either, so the floating point semantic
140 // cannot be used to perform such a rescaling.
142 APSInt MaxInt
= APFixedPoint::getMax(*this).getValue();
143 APFloat
F(FloatSema
);
144 APFloat::opStatus Status
= F
.convertFromAPInt(MaxInt
, MaxInt
.isSigned(),
145 APFloat::rmNearestTiesToAway
);
146 if ((Status
& APFloat::opOverflow
) || !isSigned())
147 return !(Status
& APFloat::opOverflow
);
149 APSInt MinInt
= APFixedPoint::getMin(*this).getValue();
150 Status
= F
.convertFromAPInt(MinInt
, MinInt
.isSigned(),
151 APFloat::rmNearestTiesToAway
);
152 return !(Status
& APFloat::opOverflow
);
155 FixedPointSemantics
FixedPointSemantics::getCommonSemantics(
156 const FixedPointSemantics
&Other
) const {
157 int CommonLsb
= std::min(getLsbWeight(), Other
.getLsbWeight());
158 int CommonMSb
= std::max(getMsbWeight() - hasSignOrPaddingBit(),
159 Other
.getMsbWeight() - Other
.hasSignOrPaddingBit());
160 unsigned CommonWidth
= CommonMSb
- CommonLsb
+ 1;
162 bool ResultIsSigned
= isSigned() || Other
.isSigned();
163 bool ResultIsSaturated
= isSaturated() || Other
.isSaturated();
164 bool ResultHasUnsignedPadding
= false;
165 if (!ResultIsSigned
) {
166 // Both are unsigned.
167 ResultHasUnsignedPadding
= hasUnsignedPadding() &&
168 Other
.hasUnsignedPadding() && !ResultIsSaturated
;
171 // If the result is signed, add an extra bit for the sign. Otherwise, if it is
172 // unsigned and has unsigned padding, we only need to add the extra padding
173 // bit back if we are not saturating.
174 if (ResultIsSigned
|| ResultHasUnsignedPadding
)
177 return FixedPointSemantics(CommonWidth
, Lsb
{CommonLsb
}, ResultIsSigned
,
178 ResultIsSaturated
, ResultHasUnsignedPadding
);
181 APFixedPoint
APFixedPoint::add(const APFixedPoint
&Other
,
182 bool *Overflow
) const {
183 auto CommonFXSema
= Sema
.getCommonSemantics(Other
.getSemantics());
184 APFixedPoint ConvertedThis
= convert(CommonFXSema
);
185 APFixedPoint ConvertedOther
= Other
.convert(CommonFXSema
);
186 APSInt ThisVal
= ConvertedThis
.getValue();
187 APSInt OtherVal
= ConvertedOther
.getValue();
188 bool Overflowed
= false;
191 if (CommonFXSema
.isSaturated()) {
192 Result
= CommonFXSema
.isSigned() ? ThisVal
.sadd_sat(OtherVal
)
193 : ThisVal
.uadd_sat(OtherVal
);
195 Result
= ThisVal
.isSigned() ? ThisVal
.sadd_ov(OtherVal
, Overflowed
)
196 : ThisVal
.uadd_ov(OtherVal
, Overflowed
);
200 *Overflow
= Overflowed
;
202 return APFixedPoint(Result
, CommonFXSema
);
205 APFixedPoint
APFixedPoint::sub(const APFixedPoint
&Other
,
206 bool *Overflow
) const {
207 auto CommonFXSema
= Sema
.getCommonSemantics(Other
.getSemantics());
208 APFixedPoint ConvertedThis
= convert(CommonFXSema
);
209 APFixedPoint ConvertedOther
= Other
.convert(CommonFXSema
);
210 APSInt ThisVal
= ConvertedThis
.getValue();
211 APSInt OtherVal
= ConvertedOther
.getValue();
212 bool Overflowed
= false;
215 if (CommonFXSema
.isSaturated()) {
216 Result
= CommonFXSema
.isSigned() ? ThisVal
.ssub_sat(OtherVal
)
217 : ThisVal
.usub_sat(OtherVal
);
219 Result
= ThisVal
.isSigned() ? ThisVal
.ssub_ov(OtherVal
, Overflowed
)
220 : ThisVal
.usub_ov(OtherVal
, Overflowed
);
224 *Overflow
= Overflowed
;
226 return APFixedPoint(Result
, CommonFXSema
);
229 APFixedPoint
APFixedPoint::mul(const APFixedPoint
&Other
,
230 bool *Overflow
) const {
231 auto CommonFXSema
= Sema
.getCommonSemantics(Other
.getSemantics());
232 APFixedPoint ConvertedThis
= convert(CommonFXSema
);
233 APFixedPoint ConvertedOther
= Other
.convert(CommonFXSema
);
234 APSInt ThisVal
= ConvertedThis
.getValue();
235 APSInt OtherVal
= ConvertedOther
.getValue();
236 bool Overflowed
= false;
238 // Widen the LHS and RHS so we can perform a full multiplication.
239 unsigned Wide
= CommonFXSema
.getWidth() * 2;
240 if (CommonFXSema
.isSigned()) {
241 ThisVal
= ThisVal
.sext(Wide
);
242 OtherVal
= OtherVal
.sext(Wide
);
244 ThisVal
= ThisVal
.zext(Wide
);
245 OtherVal
= OtherVal
.zext(Wide
);
248 // Perform the full multiplication and downscale to get the same scale.
250 // Note that the right shifts here perform an implicit downwards rounding.
251 // This rounding could discard bits that would technically place the result
252 // outside the representable range. We interpret the spec as allowing us to
253 // perform the rounding step first, avoiding the overflow case that would
256 if (CommonFXSema
.isSigned())
257 Result
= ThisVal
.smul_ov(OtherVal
, Overflowed
)
258 .relativeAShl(CommonFXSema
.getLsbWeight());
260 Result
= ThisVal
.umul_ov(OtherVal
, Overflowed
)
261 .relativeLShl(CommonFXSema
.getLsbWeight());
262 assert(!Overflowed
&& "Full multiplication cannot overflow!");
263 Result
.setIsSigned(CommonFXSema
.isSigned());
265 // If our result lies outside of the representative range of the common
266 // semantic, we either have overflow or saturation.
267 APSInt Max
= APFixedPoint::getMax(CommonFXSema
).getValue()
269 APSInt Min
= APFixedPoint::getMin(CommonFXSema
).getValue()
271 if (CommonFXSema
.isSaturated()) {
274 else if (Result
> Max
)
277 Overflowed
= Result
< Min
|| Result
> Max
;
280 *Overflow
= Overflowed
;
282 return APFixedPoint(Result
.sextOrTrunc(CommonFXSema
.getWidth()),
286 APFixedPoint
APFixedPoint::div(const APFixedPoint
&Other
,
287 bool *Overflow
) const {
288 auto CommonFXSema
= Sema
.getCommonSemantics(Other
.getSemantics());
289 APFixedPoint ConvertedThis
= convert(CommonFXSema
);
290 APFixedPoint ConvertedOther
= Other
.convert(CommonFXSema
);
291 APSInt ThisVal
= ConvertedThis
.getValue();
292 APSInt OtherVal
= ConvertedOther
.getValue();
293 bool Overflowed
= false;
295 // Widen the LHS and RHS so we can perform a full division.
296 // Also make sure that there will be enough space for the shift below to not
299 CommonFXSema
.getWidth() * 2 + std::max(-CommonFXSema
.getMsbWeight(), 0);
300 if (CommonFXSema
.isSigned()) {
301 ThisVal
= ThisVal
.sext(Wide
);
302 OtherVal
= OtherVal
.sext(Wide
);
304 ThisVal
= ThisVal
.zext(Wide
);
305 OtherVal
= OtherVal
.zext(Wide
);
308 // Upscale to compensate for the loss of precision from division, and
309 // perform the full division.
310 if (CommonFXSema
.getLsbWeight() < 0)
311 ThisVal
= ThisVal
.shl(-CommonFXSema
.getLsbWeight());
312 else if (CommonFXSema
.getLsbWeight() > 0)
313 OtherVal
= OtherVal
.shl(CommonFXSema
.getLsbWeight());
315 if (CommonFXSema
.isSigned()) {
317 APInt::sdivrem(ThisVal
, OtherVal
, Result
, Rem
);
318 // If the quotient is negative and the remainder is nonzero, round
319 // towards negative infinity by subtracting epsilon from the result.
320 if (ThisVal
.isNegative() != OtherVal
.isNegative() && !Rem
.isZero())
323 Result
= ThisVal
.udiv(OtherVal
);
324 Result
.setIsSigned(CommonFXSema
.isSigned());
326 // If our result lies outside of the representative range of the common
327 // semantic, we either have overflow or saturation.
328 APSInt Max
= APFixedPoint::getMax(CommonFXSema
).getValue()
330 APSInt Min
= APFixedPoint::getMin(CommonFXSema
).getValue()
332 if (CommonFXSema
.isSaturated()) {
335 else if (Result
> Max
)
338 Overflowed
= Result
< Min
|| Result
> Max
;
341 *Overflow
= Overflowed
;
343 return APFixedPoint(Result
.sextOrTrunc(CommonFXSema
.getWidth()),
347 APFixedPoint
APFixedPoint::shl(unsigned Amt
, bool *Overflow
) const {
348 APSInt ThisVal
= Val
;
349 bool Overflowed
= false;
352 unsigned Wide
= Sema
.getWidth() * 2;
354 ThisVal
= ThisVal
.sext(Wide
);
356 ThisVal
= ThisVal
.zext(Wide
);
358 // Clamp the shift amount at the original width, and perform the shift.
359 Amt
= std::min(Amt
, ThisVal
.getBitWidth());
360 APSInt Result
= ThisVal
<< Amt
;
361 Result
.setIsSigned(Sema
.isSigned());
363 // If our result lies outside of the representative range of the
364 // semantic, we either have overflow or saturation.
365 APSInt Max
= APFixedPoint::getMax(Sema
).getValue().extOrTrunc(Wide
);
366 APSInt Min
= APFixedPoint::getMin(Sema
).getValue().extOrTrunc(Wide
);
367 if (Sema
.isSaturated()) {
370 else if (Result
> Max
)
373 Overflowed
= Result
< Min
|| Result
> Max
;
376 *Overflow
= Overflowed
;
378 return APFixedPoint(Result
.sextOrTrunc(Sema
.getWidth()), Sema
);
381 void APFixedPoint::toString(SmallVectorImpl
<char> &Str
) const {
382 APSInt Val
= getValue();
383 int Lsb
= getLsbWeight();
384 int OrigWidth
= getWidth();
387 APSInt IntPart
= Val
;
388 IntPart
= IntPart
.extend(IntPart
.getBitWidth() + Lsb
);
390 IntPart
.toString(Str
, /*Radix=*/10);
396 if (Val
.isSigned() && Val
.isNegative()) {
398 Val
.setIsUnsigned(true);
402 int Scale
= -getLsbWeight();
403 APSInt IntPart
= (OrigWidth
> Scale
) ? (Val
>> Scale
) : APSInt::get(0);
405 // Add 4 digits to hold the value after multiplying 10 (the radix)
406 unsigned Width
= std::max(OrigWidth
, Scale
) + 4;
407 APInt FractPart
= Val
.zextOrTrunc(Scale
).zext(Width
);
408 APInt FractPartMask
= APInt::getAllOnes(Scale
).zext(Width
);
409 APInt RadixInt
= APInt(Width
, 10);
411 IntPart
.toString(Str
, /*Radix=*/10);
414 (FractPart
* RadixInt
)
416 .toString(Str
, /*Radix=*/10, Val
.isSigned());
417 FractPart
= (FractPart
* RadixInt
) & FractPartMask
;
418 } while (FractPart
!= 0);
421 void APFixedPoint::print(raw_ostream
&OS
) const {
422 OS
<< "APFixedPoint(" << toString() << ", {";
426 LLVM_DUMP_METHOD
void APFixedPoint::dump() const { print(llvm::errs()); }
428 APFixedPoint
APFixedPoint::negate(bool *Overflow
) const {
429 if (!isSaturated()) {
432 (!isSigned() && Val
!= 0) || (isSigned() && Val
.isMinSignedValue());
433 return APFixedPoint(-Val
, Sema
);
436 // We never overflow for saturation
441 return Val
.isMinSignedValue() ? getMax(Sema
) : APFixedPoint(-Val
, Sema
);
443 return APFixedPoint(Sema
);
446 APSInt
APFixedPoint::convertToInt(unsigned DstWidth
, bool DstSign
,
447 bool *Overflow
) const {
448 APSInt Result
= getIntPart();
449 unsigned SrcWidth
= getWidth();
451 APSInt DstMin
= APSInt::getMinValue(DstWidth
, !DstSign
);
452 APSInt DstMax
= APSInt::getMaxValue(DstWidth
, !DstSign
);
454 if (SrcWidth
< DstWidth
) {
455 Result
= Result
.extend(DstWidth
);
456 } else if (SrcWidth
> DstWidth
) {
457 DstMin
= DstMin
.extend(SrcWidth
);
458 DstMax
= DstMax
.extend(SrcWidth
);
462 if (Result
.isSigned() && !DstSign
) {
463 *Overflow
= Result
.isNegative() || Result
.ugt(DstMax
);
464 } else if (Result
.isUnsigned() && DstSign
) {
465 *Overflow
= Result
.ugt(DstMax
);
467 *Overflow
= Result
< DstMin
|| Result
> DstMax
;
471 Result
.setIsSigned(DstSign
);
472 return Result
.extOrTrunc(DstWidth
);
475 const fltSemantics
*APFixedPoint::promoteFloatSemantics(const fltSemantics
*S
) {
476 if (S
== &APFloat::BFloat())
477 return &APFloat::IEEEdouble();
478 else if (S
== &APFloat::IEEEhalf())
479 return &APFloat::IEEEsingle();
480 else if (S
== &APFloat::IEEEsingle())
481 return &APFloat::IEEEdouble();
482 else if (S
== &APFloat::IEEEdouble())
483 return &APFloat::IEEEquad();
484 llvm_unreachable("Could not promote float type!");
487 APFloat
APFixedPoint::convertToFloat(const fltSemantics
&FloatSema
) const {
488 // For some operations, rounding mode has an effect on the result, while
489 // other operations are lossless and should never result in rounding.
490 // To signify which these operations are, we define two rounding modes here.
491 APFloat::roundingMode RM
= APFloat::rmNearestTiesToEven
;
492 APFloat::roundingMode LosslessRM
= APFloat::rmTowardZero
;
494 // Make sure that we are operating in a type that works with this fixed-point
496 const fltSemantics
*OpSema
= &FloatSema
;
497 while (!Sema
.fitsInFloatSemantics(*OpSema
))
498 OpSema
= promoteFloatSemantics(OpSema
);
500 // Convert the fixed point value bits as an integer. If the floating point
501 // value does not have the required precision, we will round according to the
503 APFloat
Flt(*OpSema
);
504 APFloat::opStatus S
= Flt
.convertFromAPInt(Val
, Sema
.isSigned(), RM
);
506 // If we cared about checking for precision loss, we could look at this
510 // Scale down the integer value in the float to match the correct scaling
512 APFloat
ScaleFactor(std::pow(2, Sema
.getLsbWeight()));
514 ScaleFactor
.convert(*OpSema
, LosslessRM
, &Ignored
);
515 Flt
.multiply(ScaleFactor
, LosslessRM
);
517 if (OpSema
!= &FloatSema
)
518 Flt
.convert(FloatSema
, RM
, &Ignored
);
523 APFixedPoint
APFixedPoint::getFromIntValue(const APSInt
&Value
,
524 const FixedPointSemantics
&DstFXSema
,
526 FixedPointSemantics IntFXSema
= FixedPointSemantics::GetIntegerSemantics(
527 Value
.getBitWidth(), Value
.isSigned());
528 return APFixedPoint(Value
, IntFXSema
).convert(DstFXSema
, Overflow
);
532 APFixedPoint::getFromFloatValue(const APFloat
&Value
,
533 const FixedPointSemantics
&DstFXSema
,
535 // For some operations, rounding mode has an effect on the result, while
536 // other operations are lossless and should never result in rounding.
537 // To signify which these operations are, we define two rounding modes here,
538 // even though they are the same mode.
539 APFloat::roundingMode RM
= APFloat::rmTowardZero
;
540 APFloat::roundingMode LosslessRM
= APFloat::rmTowardZero
;
542 const fltSemantics
&FloatSema
= Value
.getSemantics();
545 // Handle NaN immediately.
548 return APFixedPoint(DstFXSema
);
551 // Make sure that we are operating in a type that works with this fixed-point
553 const fltSemantics
*OpSema
= &FloatSema
;
554 while (!DstFXSema
.fitsInFloatSemantics(*OpSema
))
555 OpSema
= promoteFloatSemantics(OpSema
);
560 if (&FloatSema
!= OpSema
)
561 Val
.convert(*OpSema
, LosslessRM
, &Ignored
);
563 // Scale up the float so that the 'fractional' part of the mantissa ends up in
564 // the integer range instead. Rounding mode is irrelevant here.
565 // It is fine if this overflows to infinity even for saturating types,
566 // since we will use floating point comparisons to check for saturation.
567 APFloat
ScaleFactor(std::pow(2, -DstFXSema
.getLsbWeight()));
568 ScaleFactor
.convert(*OpSema
, LosslessRM
, &Ignored
);
569 Val
.multiply(ScaleFactor
, LosslessRM
);
571 // Convert to the integral representation of the value. This rounding mode
573 APSInt
Res(DstFXSema
.getWidth(), !DstFXSema
.isSigned());
574 Val
.convertToInteger(Res
, RM
, &Ignored
);
576 // Round the integral value and scale back. This makes the
577 // overflow calculations below work properly. If we do not round here,
578 // we risk checking for overflow with a value that is outside the
579 // representable range of the fixed-point semantic even though no overflow
580 // would occur had we rounded first.
581 ScaleFactor
= APFloat(std::pow(2, DstFXSema
.getLsbWeight()));
582 ScaleFactor
.convert(*OpSema
, LosslessRM
, &Ignored
);
583 Val
.roundToIntegral(RM
);
584 Val
.multiply(ScaleFactor
, LosslessRM
);
586 // Check for overflow/saturation by checking if the floating point value
587 // is outside the range representable by the fixed-point value.
588 APFloat FloatMax
= getMax(DstFXSema
).convertToFloat(*OpSema
);
589 APFloat FloatMin
= getMin(DstFXSema
).convertToFloat(*OpSema
);
590 bool Overflowed
= false;
591 if (DstFXSema
.isSaturated()) {
593 Res
= getMax(DstFXSema
).getValue();
594 else if (Val
< FloatMin
)
595 Res
= getMin(DstFXSema
).getValue();
597 Overflowed
= Val
> FloatMax
|| Val
< FloatMin
;
600 *Overflow
= Overflowed
;
602 return APFixedPoint(Res
, DstFXSema
);