1//===- APFixedPoint.cpp - Fixed point constant handling ---------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file
10/// Defines the implementation for the fixed point number interface.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/ADT/APFixedPoint.h"
15#include "llvm/ADT/APFloat.h"
16
17#include <cmath>
18
19namespace llvm {
20
21void 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;
30}
31
32APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema,
33 bool *Overflow) const {
34 APSInt NewVal = Val;
35 int RelativeUpscale = getLsbWeight() - DstSema.getLsbWeight();
36 if (Overflow)
37 *Overflow = false;
38
39 if (RelativeUpscale > 0)
40 NewVal = NewVal.extend(width: NewVal.getBitWidth() + RelativeUpscale);
41 NewVal = NewVal.relativeShl(Amt: RelativeUpscale);
42
43 auto Mask = APInt::getBitsSetFrom(
44 numBits: NewVal.getBitWidth(),
45 loBit: std::min(a: DstSema.getIntegralBits() - DstSema.getLsbWeight(),
46 b: NewVal.getBitWidth()));
47 APInt Masked(NewVal & Mask);
48
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;
54 else if (Overflow)
55 *Overflow = true;
56 }
57
58 // If the dst semantics are unsigned, but our value is signed and negative, we
59 // clamp to zero.
60 if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) {
61 // Found negative overflow for unsigned result
62 if (DstSema.isSaturated())
63 NewVal = 0;
64 else if (Overflow)
65 *Overflow = true;
66 }
67
68 NewVal = NewVal.extOrTrunc(width: DstSema.getWidth());
69 NewVal.setIsSigned(DstSema.isSigned());
70 return APFixedPoint(NewVal, DstSema);
71}
72
73int 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();
78
79 int CommonLsb = std::min(a: getLsbWeight(), b: Other.getLsbWeight());
80 int CommonMsb = std::max(a: getMsbWeight(), b: Other.getMsbWeight());
81 unsigned CommonWidth = CommonMsb - CommonLsb + 1;
82
83 ThisVal = ThisVal.extOrTrunc(width: CommonWidth);
84 OtherVal = OtherVal.extOrTrunc(width: CommonWidth);
85
86 ThisVal = ThisVal.shl(shiftAmt: getLsbWeight() - CommonLsb);
87 OtherVal = OtherVal.shl(shiftAmt: Other.getLsbWeight() - CommonLsb);
88
89 if (ThisSigned && OtherSigned) {
90 if (ThisVal.sgt(RHS: OtherVal))
91 return 1;
92 else if (ThisVal.slt(RHS: OtherVal))
93 return -1;
94 } else if (!ThisSigned && !OtherSigned) {
95 if (ThisVal.ugt(RHS: OtherVal))
96 return 1;
97 else if (ThisVal.ult(RHS: OtherVal))
98 return -1;
99 } else if (ThisSigned && !OtherSigned) {
100 if (ThisVal.isSignBitSet())
101 return -1;
102 else if (ThisVal.ugt(RHS: OtherVal))
103 return 1;
104 else if (ThisVal.ult(RHS: OtherVal))
105 return -1;
106 } else {
107 // !ThisSigned && OtherSigned
108 if (OtherVal.isSignBitSet())
109 return 1;
110 else if (ThisVal.ugt(RHS: OtherVal))
111 return 1;
112 else if (ThisVal.ult(RHS: OtherVal))
113 return -1;
114 }
115
116 return 0;
117}
118
119APFixedPoint APFixedPoint::getMax(const FixedPointSemantics &Sema) {
120 bool IsUnsigned = !Sema.isSigned();
121 auto Val = APSInt::getMaxValue(numBits: Sema.getWidth(), Unsigned: IsUnsigned);
122 if (IsUnsigned && Sema.hasUnsignedPadding())
123 Val = Val.lshr(shiftAmt: 1);
124 return APFixedPoint(Val, Sema);
125}
126
127APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {
128 auto Val = APSInt::getMinValue(numBits: Sema.getWidth(), Unsigned: !Sema.isSigned());
129 return APFixedPoint(Val, Sema);
130}
131
132APFixedPoint APFixedPoint::getEpsilon(const FixedPointSemantics &Sema) {
133 APSInt Val(Sema.getWidth(), !Sema.isSigned());
134 Val.setBit(/*BitPosition=*/0);
135 return APFixedPoint(Val, Sema);
136}
137
138bool FixedPointSemantics::fitsInFloatSemantics(
139 const fltSemantics &FloatSema) const {
140 // A fixed point semantic fits in a floating point semantic if the maximum
141 // and minimum values as integers of the fixed point semantic can fit in the
142 // floating point semantic.
143
144 // If these values do not fit, then a floating point rescaling of the true
145 // maximum/minimum value will not fit either, so the floating point semantic
146 // cannot be used to perform such a rescaling.
147
148 APSInt MaxInt = APFixedPoint::getMax(Sema: *this).getValue();
149 APFloat F(FloatSema);
150 APFloat::opStatus Status = F.convertFromAPInt(Input: MaxInt, IsSigned: MaxInt.isSigned(),
151 RM: APFloat::rmNearestTiesToAway);
152 if ((Status & APFloat::opOverflow) || !isSigned())
153 return !(Status & APFloat::opOverflow);
154
155 APSInt MinInt = APFixedPoint::getMin(Sema: *this).getValue();
156 Status = F.convertFromAPInt(Input: MinInt, IsSigned: MinInt.isSigned(),
157 RM: APFloat::rmNearestTiesToAway);
158 return !(Status & APFloat::opOverflow);
159}
160
161FixedPointSemantics FixedPointSemantics::getCommonSemantics(
162 const FixedPointSemantics &Other) const {
163 int CommonLsb = std::min(a: getLsbWeight(), b: Other.getLsbWeight());
164 int CommonMSb = std::max(a: getMsbWeight() - hasSignOrPaddingBit(),
165 b: Other.getMsbWeight() - Other.hasSignOrPaddingBit());
166 unsigned CommonWidth = CommonMSb - CommonLsb + 1;
167
168 bool ResultIsSigned = isSigned() || Other.isSigned();
169 bool ResultIsSaturated = isSaturated() || Other.isSaturated();
170 bool ResultHasUnsignedPadding = false;
171 if (!ResultIsSigned) {
172 // Both are unsigned.
173 ResultHasUnsignedPadding = hasUnsignedPadding() &&
174 Other.hasUnsignedPadding() && !ResultIsSaturated;
175 }
176
177 // If the result is signed, add an extra bit for the sign. Otherwise, if it is
178 // unsigned and has unsigned padding, we only need to add the extra padding
179 // bit back if we are not saturating.
180 if (ResultIsSigned || ResultHasUnsignedPadding)
181 CommonWidth++;
182
183 return FixedPointSemantics(CommonWidth, Lsb{.LsbWeight: CommonLsb}, ResultIsSigned,
184 ResultIsSaturated, ResultHasUnsignedPadding);
185}
186
187APFixedPoint APFixedPoint::add(const APFixedPoint &Other,
188 bool *Overflow) const {
189 auto CommonFXSema = Sema.getCommonSemantics(Other: Other.getSemantics());
190 APFixedPoint ConvertedThis = convert(DstSema: CommonFXSema);
191 APFixedPoint ConvertedOther = Other.convert(DstSema: CommonFXSema);
192 APSInt ThisVal = ConvertedThis.getValue();
193 APSInt OtherVal = ConvertedOther.getValue();
194 bool Overflowed = false;
195
196 APSInt Result;
197 if (CommonFXSema.isSaturated()) {
198 Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(RHS: OtherVal)
199 : ThisVal.uadd_sat(RHS: OtherVal);
200 } else {
201 Result = ThisVal.isSigned() ? ThisVal.sadd_ov(RHS: OtherVal, Overflow&: Overflowed)
202 : ThisVal.uadd_ov(RHS: OtherVal, Overflow&: Overflowed);
203 }
204
205 if (Overflow)
206 *Overflow = Overflowed;
207
208 return APFixedPoint(Result, CommonFXSema);
209}
210
211APFixedPoint APFixedPoint::sub(const APFixedPoint &Other,
212 bool *Overflow) const {
213 auto CommonFXSema = Sema.getCommonSemantics(Other: Other.getSemantics());
214 APFixedPoint ConvertedThis = convert(DstSema: CommonFXSema);
215 APFixedPoint ConvertedOther = Other.convert(DstSema: CommonFXSema);
216 APSInt ThisVal = ConvertedThis.getValue();
217 APSInt OtherVal = ConvertedOther.getValue();
218 bool Overflowed = false;
219
220 APSInt Result;
221 if (CommonFXSema.isSaturated()) {
222 Result = CommonFXSema.isSigned() ? ThisVal.ssub_sat(RHS: OtherVal)
223 : ThisVal.usub_sat(RHS: OtherVal);
224 } else {
225 Result = ThisVal.isSigned() ? ThisVal.ssub_ov(RHS: OtherVal, Overflow&: Overflowed)
226 : ThisVal.usub_ov(RHS: OtherVal, Overflow&: Overflowed);
227 }
228
229 if (Overflow)
230 *Overflow = Overflowed;
231
232 return APFixedPoint(Result, CommonFXSema);
233}
234
235APFixedPoint APFixedPoint::mul(const APFixedPoint &Other,
236 bool *Overflow) const {
237 auto CommonFXSema = Sema.getCommonSemantics(Other: Other.getSemantics());
238 APFixedPoint ConvertedThis = convert(DstSema: CommonFXSema);
239 APFixedPoint ConvertedOther = Other.convert(DstSema: CommonFXSema);
240 APSInt ThisVal = ConvertedThis.getValue();
241 APSInt OtherVal = ConvertedOther.getValue();
242 bool Overflowed = false;
243
244 // Widen the LHS and RHS so we can perform a full multiplication.
245 unsigned Wide = CommonFXSema.getWidth() * 2;
246 if (CommonFXSema.isSigned()) {
247 ThisVal = ThisVal.sext(width: Wide);
248 OtherVal = OtherVal.sext(width: Wide);
249 } else {
250 ThisVal = ThisVal.zext(width: Wide);
251 OtherVal = OtherVal.zext(width: Wide);
252 }
253
254 // Perform the full multiplication and downscale to get the same scale.
255 //
256 // Note that the right shifts here perform an implicit downwards rounding.
257 // This rounding could discard bits that would technically place the result
258 // outside the representable range. We interpret the spec as allowing us to
259 // perform the rounding step first, avoiding the overflow case that would
260 // arise.
261 APSInt Result;
262 if (CommonFXSema.isSigned())
263 Result = ThisVal.smul_ov(RHS: OtherVal, Overflow&: Overflowed)
264 .relativeAShl(RelativeShift: CommonFXSema.getLsbWeight());
265 else
266 Result = ThisVal.umul_ov(RHS: OtherVal, Overflow&: Overflowed)
267 .relativeLShl(RelativeShift: CommonFXSema.getLsbWeight());
268 assert(!Overflowed && "Full multiplication cannot overflow!");
269 Result.setIsSigned(CommonFXSema.isSigned());
270
271 // If our result lies outside of the representative range of the common
272 // semantic, we either have overflow or saturation.
273 APSInt Max = APFixedPoint::getMax(Sema: CommonFXSema).getValue()
274 .extOrTrunc(width: Wide);
275 APSInt Min = APFixedPoint::getMin(Sema: CommonFXSema).getValue()
276 .extOrTrunc(width: Wide);
277 if (CommonFXSema.isSaturated()) {
278 if (Result < Min)
279 Result = Min;
280 else if (Result > Max)
281 Result = Max;
282 } else
283 Overflowed = Result < Min || Result > Max;
284
285 if (Overflow)
286 *Overflow = Overflowed;
287
288 return APFixedPoint(Result.sextOrTrunc(width: CommonFXSema.getWidth()),
289 CommonFXSema);
290}
291
292APFixedPoint APFixedPoint::div(const APFixedPoint &Other,
293 bool *Overflow) const {
294 auto CommonFXSema = Sema.getCommonSemantics(Other: Other.getSemantics());
295 APFixedPoint ConvertedThis = convert(DstSema: CommonFXSema);
296 APFixedPoint ConvertedOther = Other.convert(DstSema: CommonFXSema);
297 APSInt ThisVal = ConvertedThis.getValue();
298 APSInt OtherVal = ConvertedOther.getValue();
299 bool Overflowed = false;
300
301 // Widen the LHS and RHS so we can perform a full division.
302 // Also make sure that there will be enough space for the shift below to not
303 // overflow
304 unsigned Wide =
305 CommonFXSema.getWidth() * 2 + std::max(a: -CommonFXSema.getMsbWeight(), b: 0);
306 if (CommonFXSema.isSigned()) {
307 ThisVal = ThisVal.sext(width: Wide);
308 OtherVal = OtherVal.sext(width: Wide);
309 } else {
310 ThisVal = ThisVal.zext(width: Wide);
311 OtherVal = OtherVal.zext(width: Wide);
312 }
313
314 // Upscale to compensate for the loss of precision from division, and
315 // perform the full division.
316 if (CommonFXSema.getLsbWeight() < 0)
317 ThisVal = ThisVal.shl(shiftAmt: -CommonFXSema.getLsbWeight());
318 else if (CommonFXSema.getLsbWeight() > 0)
319 OtherVal = OtherVal.shl(shiftAmt: CommonFXSema.getLsbWeight());
320 APSInt Result;
321 if (CommonFXSema.isSigned()) {
322 APInt Rem;
323 APInt::sdivrem(LHS: ThisVal, RHS: OtherVal, Quotient&: Result, Remainder&: Rem);
324 // If the quotient is negative and the remainder is nonzero, round
325 // towards negative infinity by subtracting epsilon from the result.
326 if (ThisVal.isNegative() != OtherVal.isNegative() && !Rem.isZero())
327 Result = Result - 1;
328 } else
329 Result = ThisVal.udiv(RHS: OtherVal);
330 Result.setIsSigned(CommonFXSema.isSigned());
331
332 // If our result lies outside of the representative range of the common
333 // semantic, we either have overflow or saturation.
334 APSInt Max = APFixedPoint::getMax(Sema: CommonFXSema).getValue()
335 .extOrTrunc(width: Wide);
336 APSInt Min = APFixedPoint::getMin(Sema: CommonFXSema).getValue()
337 .extOrTrunc(width: Wide);
338 if (CommonFXSema.isSaturated()) {
339 if (Result < Min)
340 Result = Min;
341 else if (Result > Max)
342 Result = Max;
343 } else
344 Overflowed = Result < Min || Result > Max;
345
346 if (Overflow)
347 *Overflow = Overflowed;
348
349 return APFixedPoint(Result.sextOrTrunc(width: CommonFXSema.getWidth()),
350 CommonFXSema);
351}
352
353APFixedPoint APFixedPoint::shl(unsigned Amt, bool *Overflow) const {
354 APSInt ThisVal = Val;
355 bool Overflowed = false;
356
357 // Widen the LHS.
358 unsigned Wide = Sema.getWidth() * 2;
359 if (Sema.isSigned())
360 ThisVal = ThisVal.sext(width: Wide);
361 else
362 ThisVal = ThisVal.zext(width: Wide);
363
364 // Clamp the shift amount at the original width, and perform the shift.
365 Amt = std::min(a: Amt, b: ThisVal.getBitWidth());
366 APSInt Result = ThisVal << Amt;
367 Result.setIsSigned(Sema.isSigned());
368
369 // If our result lies outside of the representative range of the
370 // semantic, we either have overflow or saturation.
371 APSInt Max = APFixedPoint::getMax(Sema).getValue().extOrTrunc(width: Wide);
372 APSInt Min = APFixedPoint::getMin(Sema).getValue().extOrTrunc(width: Wide);
373 if (Sema.isSaturated()) {
374 if (Result < Min)
375 Result = Min;
376 else if (Result > Max)
377 Result = Max;
378 } else
379 Overflowed = Result < Min || Result > Max;
380
381 if (Overflow)
382 *Overflow = Overflowed;
383
384 return APFixedPoint(Result.sextOrTrunc(width: Sema.getWidth()), Sema);
385}
386
387void APFixedPoint::toString(SmallVectorImpl<char> &Str) const {
388 APSInt Val = getValue();
389 int Lsb = getLsbWeight();
390 int OrigWidth = getWidth();
391
392 if (Lsb >= 0) {
393 APSInt IntPart = Val;
394 IntPart = IntPart.extend(width: IntPart.getBitWidth() + Lsb);
395 IntPart <<= Lsb;
396 IntPart.toString(Str, /*Radix=*/10);
397 Str.push_back(Elt: '.');
398 Str.push_back(Elt: '0');
399 return;
400 }
401
402 if (Val.isSigned() && Val.isNegative()) {
403 Val = -Val;
404 Val.setIsUnsigned(true);
405 Str.push_back(Elt: '-');
406 }
407
408 int Scale = -getLsbWeight();
409 APSInt IntPart = (OrigWidth > Scale) ? (Val >> Scale) : APSInt::get(X: 0);
410
411 // Add 4 digits to hold the value after multiplying 10 (the radix)
412 unsigned Width = std::max(a: OrigWidth, b: Scale) + 4;
413 APInt FractPart = Val.zextOrTrunc(width: Scale).zext(width: Width);
414 APInt FractPartMask = APInt::getAllOnes(numBits: Scale).zext(width: Width);
415 APInt RadixInt = APInt(Width, 10);
416
417 IntPart.toString(Str, /*Radix=*/10);
418 Str.push_back(Elt: '.');
419 do {
420 (FractPart * RadixInt)
421 .lshr(shiftAmt: Scale)
422 .toString(Str, /*Radix=*/10, Signed: Val.isSigned());
423 FractPart = (FractPart * RadixInt) & FractPartMask;
424 } while (FractPart != 0);
425}
426
427void APFixedPoint::print(raw_ostream &OS) const {
428 OS << "APFixedPoint(" << toString() << ", {";
429 Sema.print(OS);
430 OS << "})";
431}
432LLVM_DUMP_METHOD void APFixedPoint::dump() const { print(OS&: llvm::errs()); }
433
434APFixedPoint APFixedPoint::negate(bool *Overflow) const {
435 if (!isSaturated()) {
436 if (Overflow)
437 *Overflow =
438 (!isSigned() && Val != 0) || (isSigned() && Val.isMinSignedValue());
439 return APFixedPoint(-Val, Sema);
440 }
441
442 // We never overflow for saturation
443 if (Overflow)
444 *Overflow = false;
445
446 if (isSigned())
447 return Val.isMinSignedValue() ? getMax(Sema) : APFixedPoint(-Val, Sema);
448 else
449 return APFixedPoint(Sema);
450}
451
452APSInt APFixedPoint::convertToInt(unsigned DstWidth, bool DstSign,
453 bool *Overflow) const {
454 APSInt Result = getIntPart();
455 unsigned SrcWidth = getWidth();
456
457 APSInt DstMin = APSInt::getMinValue(numBits: DstWidth, Unsigned: !DstSign);
458 APSInt DstMax = APSInt::getMaxValue(numBits: DstWidth, Unsigned: !DstSign);
459
460 if (SrcWidth < DstWidth) {
461 Result = Result.extend(width: DstWidth);
462 } else if (SrcWidth > DstWidth) {
463 DstMin = DstMin.extend(width: SrcWidth);
464 DstMax = DstMax.extend(width: SrcWidth);
465 }
466
467 if (Overflow) {
468 if (Result.isSigned() && !DstSign) {
469 *Overflow = Result.isNegative() || Result.ugt(RHS: DstMax);
470 } else if (Result.isUnsigned() && DstSign) {
471 *Overflow = Result.ugt(RHS: DstMax);
472 } else {
473 *Overflow = Result < DstMin || Result > DstMax;
474 }
475 }
476
477 Result.setIsSigned(DstSign);
478 return Result.extOrTrunc(width: DstWidth);
479}
480
481const fltSemantics *APFixedPoint::promoteFloatSemantics(const fltSemantics *S) {
482 if (S == &APFloat::BFloat())
483 return &APFloat::IEEEdouble();
484 else if (S == &APFloat::IEEEhalf())
485 return &APFloat::IEEEsingle();
486 else if (S == &APFloat::IEEEsingle())
487 return &APFloat::IEEEdouble();
488 else if (S == &APFloat::IEEEdouble())
489 return &APFloat::IEEEquad();
490 llvm_unreachable("Could not promote float type!");
491}
492
493APFloat APFixedPoint::convertToFloat(const fltSemantics &FloatSema) const {
494 // For some operations, rounding mode has an effect on the result, while
495 // other operations are lossless and should never result in rounding.
496 // To signify which these operations are, we define two rounding modes here.
497 APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;
498 APFloat::roundingMode LosslessRM = APFloat::rmTowardZero;
499
500 // Make sure that we are operating in a type that works with this fixed-point
501 // semantic.
502 const fltSemantics *OpSema = &FloatSema;
503 while (!Sema.fitsInFloatSemantics(FloatSema: *OpSema))
504 OpSema = promoteFloatSemantics(S: OpSema);
505
506 // Convert the fixed point value bits as an integer. If the floating point
507 // value does not have the required precision, we will round according to the
508 // given mode.
509 APFloat Flt(*OpSema);
510 APFloat::opStatus S = Flt.convertFromAPInt(Input: Val, IsSigned: Sema.isSigned(), RM);
511
512 // If we cared about checking for precision loss, we could look at this
513 // status.
514 (void)S;
515
516 // Scale down the integer value in the float to match the correct scaling
517 // factor.
518 APFloat ScaleFactor(std::pow(x: 2, y: Sema.getLsbWeight()));
519 bool Ignored;
520 ScaleFactor.convert(ToSemantics: *OpSema, RM: LosslessRM, losesInfo: &Ignored);
521 Flt.multiply(RHS: ScaleFactor, RM: LosslessRM);
522
523 if (OpSema != &FloatSema)
524 Flt.convert(ToSemantics: FloatSema, RM, losesInfo: &Ignored);
525
526 return Flt;
527}
528
529APFixedPoint APFixedPoint::getFromIntValue(const APSInt &Value,
530 const FixedPointSemantics &DstFXSema,
531 bool *Overflow) {
532 FixedPointSemantics IntFXSema = FixedPointSemantics::GetIntegerSemantics(
533 Width: Value.getBitWidth(), IsSigned: Value.isSigned());
534 return APFixedPoint(Value, IntFXSema).convert(DstSema: DstFXSema, Overflow);
535}
536
537APFixedPoint
538APFixedPoint::getFromFloatValue(const APFloat &Value,
539 const FixedPointSemantics &DstFXSema,
540 bool *Overflow) {
541 // For some operations, rounding mode has an effect on the result, while
542 // other operations are lossless and should never result in rounding.
543 // To signify which these operations are, we define two rounding modes here,
544 // even though they are the same mode.
545 APFloat::roundingMode RM = APFloat::rmTowardZero;
546 APFloat::roundingMode LosslessRM = APFloat::rmTowardZero;
547
548 const fltSemantics &FloatSema = Value.getSemantics();
549
550 if (Value.isNaN()) {
551 // Handle NaN immediately.
552 if (Overflow)
553 *Overflow = true;
554 return APFixedPoint(DstFXSema);
555 }
556
557 // Make sure that we are operating in a type that works with this fixed-point
558 // semantic.
559 const fltSemantics *OpSema = &FloatSema;
560 while (!DstFXSema.fitsInFloatSemantics(FloatSema: *OpSema))
561 OpSema = promoteFloatSemantics(S: OpSema);
562
563 APFloat Val = Value;
564
565 bool Ignored;
566 if (&FloatSema != OpSema)
567 Val.convert(ToSemantics: *OpSema, RM: LosslessRM, losesInfo: &Ignored);
568
569 // Scale up the float so that the 'fractional' part of the mantissa ends up in
570 // the integer range instead. Rounding mode is irrelevant here.
571 // It is fine if this overflows to infinity even for saturating types,
572 // since we will use floating point comparisons to check for saturation.
573 APFloat ScaleFactor(std::pow(x: 2, y: -DstFXSema.getLsbWeight()));
574 ScaleFactor.convert(ToSemantics: *OpSema, RM: LosslessRM, losesInfo: &Ignored);
575 Val.multiply(RHS: ScaleFactor, RM: LosslessRM);
576
577 // Convert to the integral representation of the value. This rounding mode
578 // is significant.
579 APSInt Res(DstFXSema.getWidth(), !DstFXSema.isSigned());
580 Val.convertToInteger(Result&: Res, RM, IsExact: &Ignored);
581
582 // Round the integral value and scale back. This makes the
583 // overflow calculations below work properly. If we do not round here,
584 // we risk checking for overflow with a value that is outside the
585 // representable range of the fixed-point semantic even though no overflow
586 // would occur had we rounded first.
587 ScaleFactor = APFloat(std::pow(x: 2, y: DstFXSema.getLsbWeight()));
588 ScaleFactor.convert(ToSemantics: *OpSema, RM: LosslessRM, losesInfo: &Ignored);
589 Val.roundToIntegral(RM);
590 Val.multiply(RHS: ScaleFactor, RM: LosslessRM);
591
592 // Check for overflow/saturation by checking if the floating point value
593 // is outside the range representable by the fixed-point value.
594 APFloat FloatMax = getMax(Sema: DstFXSema).convertToFloat(FloatSema: *OpSema);
595 APFloat FloatMin = getMin(Sema: DstFXSema).convertToFloat(FloatSema: *OpSema);
596 bool Overflowed = false;
597 if (DstFXSema.isSaturated()) {
598 if (Val > FloatMax)
599 Res = getMax(Sema: DstFXSema).getValue();
600 else if (Val < FloatMin)
601 Res = getMin(Sema: DstFXSema).getValue();
602 } else
603 Overflowed = Val > FloatMax || Val < FloatMin;
604
605 if (Overflow)
606 *Overflow = Overflowed;
607
608 return APFixedPoint(Res, DstFXSema);
609}
610
611} // namespace llvm
612