Skip to content

Commit f4066fa

Browse files
authored
[clang] Use constant rounding mode for floating literals (llvm#90877)
Conversion of floating-point literal to binary representation must be made using constant rounding mode, which can be changed using pragma FENV_ROUND. For example, the literal "0.1F" should be representes by either 0.099999994 or 0.100000001 depending on the rounding direction.
1 parent a26fbf3 commit f4066fa

File tree

5 files changed

+122
-10
lines changed

5 files changed

+122
-10
lines changed

clang/include/clang/Lex/LiteralSupport.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,10 @@ class NumericLiteralParser {
118118
/// bits of the result and return true. Otherwise, return false.
119119
bool GetIntegerValue(llvm::APInt &Val);
120120

121-
/// GetFloatValue - Convert this numeric literal to a floating value, using
122-
/// the specified APFloat fltSemantics (specifying float, double, etc).
123-
/// The optional bool isExact (passed-by-reference) has its value
124-
/// set to true if the returned APFloat can represent the number in the
125-
/// literal exactly, and false otherwise.
126-
llvm::APFloat::opStatus GetFloatValue(llvm::APFloat &Result);
121+
/// Convert this numeric literal to a floating value, using the specified
122+
/// APFloat fltSemantics (specifying float, double, etc) and rounding mode.
123+
llvm::APFloat::opStatus GetFloatValue(llvm::APFloat &Result,
124+
llvm::RoundingMode RM);
127125

128126
/// GetFixedPointValue - Convert this numeric literal value into a
129127
/// scaled integer that represents this value. Returns true if an overflow

clang/lib/Lex/LiteralSupport.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,8 @@ bool NumericLiteralParser::GetIntegerValue(llvm::APInt &Val) {
15201520
}
15211521

15221522
llvm::APFloat::opStatus
1523-
NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) {
1523+
NumericLiteralParser::GetFloatValue(llvm::APFloat &Result,
1524+
llvm::RoundingMode RM) {
15241525
using llvm::APFloat;
15251526

15261527
unsigned n = std::min(SuffixBegin - ThisTokBegin, ThisTokEnd - ThisTokBegin);
@@ -1534,8 +1535,7 @@ NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) {
15341535
Str = Buffer;
15351536
}
15361537

1537-
auto StatusOrErr =
1538-
Result.convertFromString(Str, APFloat::rmNearestTiesToEven);
1538+
auto StatusOrErr = Result.convertFromString(Str, RM);
15391539
assert(StatusOrErr && "Invalid floating point representation");
15401540
return !errorToBool(StatusOrErr.takeError()) ? *StatusOrErr
15411541
: APFloat::opInvalidOp;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3699,7 +3699,10 @@ static Expr *BuildFloatingLiteral(Sema &S, NumericLiteralParser &Literal,
36993699
using llvm::APFloat;
37003700
APFloat Val(Format);
37013701

3702-
APFloat::opStatus result = Literal.GetFloatValue(Val);
3702+
llvm::RoundingMode RM = S.CurFPFeatures.getRoundingMode();
3703+
if (RM == llvm::RoundingMode::Dynamic)
3704+
RM = llvm::RoundingMode::NearestTiesToEven;
3705+
APFloat::opStatus result = Literal.GetFloatValue(Val, RM);
37033706

37043707
// Overflow is always an error, but underflow is only an error if
37053708
// we underflowed to zero (APFloat reports denormals as underflow).

clang/test/AST/const-fpfeatures.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ float FI1u = 0xFFFFFFFFU;
1919
float _Complex C1u = C0;
2020
// CHECK: @C1u = {{.*}} { float, float } { float 0x3FF0000020000000, float 0x3FF0000020000000 }
2121

22+
float FLu = 0.1F;
23+
// CHECK: @FLu = {{.*}} float 0x3FB99999A0000000
24+
2225

2326
#pragma STDC FENV_ROUND FE_DOWNWARD
2427

@@ -35,3 +38,6 @@ float FI1d = 0xFFFFFFFFU;
3538

3639
float _Complex C1d = C0;
3740
// CHECK: @C1d = {{.*}} { float, float } { float 1.000000e+00, float 1.000000e+00 }
41+
42+
float FLd = 0.1F;
43+
// CHECK: @FLd = {{.*}} float 0x3FB9999980000000

clang/test/AST/const-fpfeatures.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,108 @@ float V7 = []() -> float {
7979
0x0.000001p0F);
8080
}();
8181
// CHECK: @V7 = {{.*}} float 1.000000e+00
82+
83+
#pragma STDC FENV_ROUND FE_DYNAMIC
84+
85+
template<float V> struct L {
86+
constexpr L() : value(V) {}
87+
float value;
88+
};
89+
90+
#pragma STDC FENV_ROUND FE_DOWNWARD
91+
L<0.1F> val_d;
92+
// CHECK: @val_d = {{.*}} { float 0x3FB9999980000000 }
93+
94+
#pragma STDC FENV_ROUND FE_UPWARD
95+
L<0.1F> val_u;
96+
// CHECK: @val_u = {{.*}} { float 0x3FB99999A0000000 }
97+
98+
99+
// Check literals in macros.
100+
101+
#pragma STDC FENV_ROUND FE_DOWNWARD
102+
#define CONSTANT_0_1 0.1F
103+
104+
#pragma STDC FENV_ROUND FE_UPWARD
105+
float C1_ru = CONSTANT_0_1;
106+
// CHECK: @C1_ru = {{.*}} float 0x3FB99999A0000000
107+
108+
#pragma STDC FENV_ROUND FE_DOWNWARD
109+
float C1_rd = CONSTANT_0_1;
110+
// CHECK: @C1_rd = {{.*}} float 0x3FB9999980000000
111+
112+
#pragma STDC FENV_ROUND FE_DOWNWARD
113+
#define PRAGMA(x) _Pragma(#x)
114+
#define CONSTANT_0_1_RM(v, rm) ([](){ PRAGMA(STDC FENV_ROUND rm); return v; }())
115+
116+
#pragma STDC FENV_ROUND FE_UPWARD
117+
float C2_rd = CONSTANT_0_1_RM(0.1F, FE_DOWNWARD);
118+
float C2_ru = CONSTANT_0_1_RM(0.1F, FE_UPWARD);
119+
// CHECK: @C2_rd = {{.*}} float 0x3FB9999980000000
120+
// CHECK: @C2_ru = {{.*}} float 0x3FB99999A0000000
121+
122+
#pragma STDC FENV_ROUND FE_DOWNWARD
123+
float C3_rd = CONSTANT_0_1_RM(0.1F, FE_DOWNWARD);
124+
float C3_ru = CONSTANT_0_1_RM(0.1F, FE_UPWARD);
125+
// CHECK: @C3_rd = {{.*}} float 0x3FB9999980000000
126+
// CHECK: @C3_ru = {{.*}} float 0x3FB99999A0000000
127+
128+
// Check literals in template instantiations.
129+
130+
#pragma STDC FENV_ROUND FE_DYNAMIC
131+
132+
template<typename T, T C>
133+
constexpr T foo() {
134+
return C;
135+
}
136+
137+
#pragma STDC FENV_ROUND FE_DOWNWARD
138+
float var_d = foo<float, 0.1F>();
139+
// CHECK: @var_d = {{.*}} float 0x3FB9999980000000
140+
141+
#pragma STDC FENV_ROUND FE_UPWARD
142+
float var_u = foo<float, 0.1F>();
143+
// CHECK: @var_u = {{.*}} float 0x3FB99999A0000000
144+
145+
#pragma STDC FENV_ROUND FE_DYNAMIC
146+
147+
template<typename T, T f> void foo2() {
148+
T Val = f;
149+
}
150+
151+
void func_01() {
152+
#pragma STDC FENV_ROUND FE_DOWNWARD
153+
foo2<float, 0.1f>();
154+
}
155+
156+
void func_02() {
157+
#pragma STDC FENV_ROUND FE_UPWARD
158+
foo2<float, 0.1f>();
159+
}
160+
161+
// CHECK-LABEL: define {{.*}} void @_Z4foo2IfTnT_Lf3dccccccEEvv()
162+
// CHECK: store float 0x3FB9999980000000, ptr
163+
164+
// CHECK-LABEL: define {{.*}} void @_Z4foo2IfTnT_Lf3dcccccdEEvv()
165+
// CHECK: store float 0x3FB99999A0000000, ptr
166+
167+
168+
#pragma STDC FENV_ROUND FE_DOWNWARD
169+
template <int C>
170+
float tfunc_01() {
171+
return 0.1F; // Must be 0x3FB9999980000000 in all instantiations.
172+
}
173+
template float tfunc_01<0>();
174+
// CHECK-LABEL: define {{.*}} float @_Z8tfunc_01ILi0EEfv()
175+
// CHECK: ret float 0x3FB9999980000000
176+
177+
#pragma STDC FENV_ROUND FE_UPWARD
178+
template float tfunc_01<1>();
179+
// CHECK-LABEL: define {{.*}} float @_Z8tfunc_01ILi1EEfv()
180+
// CHECK: ret float 0x3FB9999980000000
181+
182+
template<> float tfunc_01<2>() {
183+
return 0.1F;
184+
}
185+
// CHECK-LABEL: define {{.*}} float @_Z8tfunc_01ILi2EEfv()
186+
// CHECK: ret float 0x3FB99999A0000000

0 commit comments

Comments
 (0)