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 | |
19 | namespace llvm { |
20 | |
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; |
30 | } |
31 | |
32 | APFixedPoint 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 | |
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(); |
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 | |
119 | APFixedPoint 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 | |
127 | APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) { |
128 | auto Val = APSInt::getMinValue(numBits: Sema.getWidth(), Unsigned: !Sema.isSigned()); |
129 | return APFixedPoint(Val, Sema); |
130 | } |
131 | |
132 | APFixedPoint APFixedPoint::getEpsilon(const FixedPointSemantics &Sema) { |
133 | APSInt Val(Sema.getWidth(), !Sema.isSigned()); |
134 | Val.setBit(/*BitPosition=*/0); |
135 | return APFixedPoint(Val, Sema); |
136 | } |
137 | |
138 | bool 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 | |
161 | FixedPointSemantics 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 | |
187 | APFixedPoint 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 | |
211 | APFixedPoint 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 | |
235 | APFixedPoint 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 | |
292 | APFixedPoint 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 | |
353 | APFixedPoint 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 | |
387 | void 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 | |
427 | void APFixedPoint::print(raw_ostream &OS) const { |
428 | OS << "APFixedPoint(" << toString() << ", {" ; |
429 | Sema.print(OS); |
430 | OS << "})" ; |
431 | } |
432 | LLVM_DUMP_METHOD void APFixedPoint::dump() const { print(OS&: llvm::errs()); } |
433 | |
434 | APFixedPoint 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 | |
452 | APSInt 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 | |
481 | const 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 | |
493 | APFloat 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 | |
529 | APFixedPoint 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 | |
537 | APFixedPoint |
538 | APFixedPoint::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 | |