-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[C++23] [CLANG] Adding C++23 constexpr math functions: fmin, fmax and frexp. #88978
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 17 commits
3acc848
d7050b5
625028a
319fdd0
da06dba
1793cf0
42d7d4c
9f4d632
efeec9b
e384a05
5f67645
49186b9
cf2814d
1c2912c
d32737e
bd536f5
32b788a
501e39a
0cc61eb
c615b22
5eec2db
299df9a
1507a33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -253,8 +253,10 @@ def FmodF16F128 : F16F128MathTemplate, Builtin { | |
|
||
def FrexpF16F128 : F16F128MathTemplate, Builtin { | ||
let Spellings = ["__builtin_frexp"]; | ||
let Attributes = [FunctionWithBuiltinPrefix, NoThrow]; | ||
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, ConstexprSince]; | ||
let Prototype = "T(T, int*)"; | ||
let BuiltinPrefixedAliasIsConstexpr = 1; | ||
let UnprefixedOnlyConstexprSince = "lang_cxx23"; | ||
} | ||
|
||
def HugeVal : Builtin, FPMathWithF128Template { | ||
|
@@ -3461,15 +3463,15 @@ def Copysign : FPMathTemplate, LibBuiltin<"math.h"> { | |
let Attributes = [NoThrow, Const]; | ||
let Prototype = "T(T, T)"; | ||
let AddBuiltinPrefixedAlias = 1; | ||
let OnlyBuiltinPrefixedAliasIsConstexpr = 1; | ||
let BuiltinPrefixedAliasIsConstexpr = 1; | ||
} | ||
|
||
def Fabs : FPMathTemplate, LibBuiltin<"math.h"> { | ||
let Spellings = ["fabs"]; | ||
let Attributes = [NoThrow, Const]; | ||
let Prototype = "T(T)"; | ||
let AddBuiltinPrefixedAlias = 1; | ||
let OnlyBuiltinPrefixedAliasIsConstexpr = 1; | ||
let BuiltinPrefixedAliasIsConstexpr = 1; | ||
} | ||
|
||
def Finite : FPMathTemplate, GNULibBuiltin<"math.h"> { | ||
|
@@ -3494,9 +3496,11 @@ def Fmod : FPMathTemplate, LibBuiltin<"math.h"> { | |
|
||
def Frexp : FPMathTemplate, LibBuiltin<"math.h"> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a separate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added |
||
let Spellings = ["frexp"]; | ||
let Attributes = [NoThrow]; | ||
let Attributes = [NoThrow, Const, ConstexprSince]; | ||
let Prototype = "T(T, int*)"; | ||
let AddBuiltinPrefixedAlias = 1; | ||
zahiraam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let BuiltinPrefixedAliasIsConstexpr = 1; | ||
let UnprefixedOnlyConstexprSince = "lang_cxx23"; | ||
} | ||
|
||
def Ldexp : FPMathTemplate, LibBuiltin<"math.h"> { | ||
|
@@ -3518,7 +3522,7 @@ def Nan : FPMathTemplate, LibBuiltin<"math.h"> { | |
let Attributes = [Pure, NoThrow]; | ||
let Prototype = "T(char const*)"; | ||
let AddBuiltinPrefixedAlias = 1; | ||
let OnlyBuiltinPrefixedAliasIsConstexpr = 1; | ||
let BuiltinPrefixedAliasIsConstexpr = 1; | ||
} | ||
|
||
def Pow : FPMathTemplate, LibBuiltin<"math.h"> { | ||
|
@@ -3664,18 +3668,20 @@ def Fma : FPMathTemplate, LibBuiltin<"math.h"> { | |
|
||
def Fmax : FPMathTemplate, LibBuiltin<"math.h"> { | ||
let Spellings = ["fmax"]; | ||
let Attributes = [NoThrow, Const]; | ||
let Attributes = [NoThrow, Const, ConstexprSince]; | ||
let Prototype = "T(T, T)"; | ||
let AddBuiltinPrefixedAlias = 1; | ||
let OnlyBuiltinPrefixedAliasIsConstexpr = 1; | ||
let BuiltinPrefixedAliasIsConstexpr = 1; | ||
let UnprefixedOnlyConstexprSince = "lang_cxx23"; | ||
} | ||
|
||
def Fmin : FPMathTemplate, LibBuiltin<"math.h"> { | ||
let Spellings = ["fmin"]; | ||
let Attributes = [NoThrow, Const]; | ||
let Attributes = [NoThrow, Const, ConstexprSince]; | ||
let Prototype = "T(T, T)"; | ||
let AddBuiltinPrefixedAlias = 1; | ||
let OnlyBuiltinPrefixedAliasIsConstexpr = 1; | ||
let BuiltinPrefixedAliasIsConstexpr = 1; | ||
let UnprefixedOnlyConstexprSince = "lang_cxx23"; | ||
} | ||
|
||
def Hypot : FPMathTemplate, LibBuiltin<"math.h"> { | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -14718,6 +14718,8 @@ class FloatExprEvaluator | |||
return true; | ||||
} | ||||
|
||||
void StoreExponent(LValue Pointer, int exp); | ||||
|
||||
bool VisitCallExpr(const CallExpr *E); | ||||
|
||||
bool VisitUnaryOperator(const UnaryOperator *E); | ||||
|
@@ -14775,6 +14777,29 @@ static bool TryEvaluateBuiltinNaN(const ASTContext &Context, | |||
|
||||
return true; | ||||
} | ||||
// Checks that the value x is in the range (-1;-0.5], [0.5; 1) | ||||
static bool isInFrexpResultRange(const llvm::APFloat &x) { | ||||
llvm::APFloat AbsX = abs(x); | ||||
|
||||
llvm::APFloat half(x.getSemantics(), "0.5"); | ||||
llvm::APFloat one(x.getSemantics(), "1.0"); | ||||
|
||||
return (AbsX.compare(half) != llvm::APFloat::cmpLessThan && | ||||
AbsX.compare(one) == llvm::APFloat::cmpLessThan); | ||||
} | ||||
|
||||
void FloatExprEvaluator::StoreExponent(LValue Pointer, int exp) { | ||||
const APValue::LValueBase Base = Pointer.getLValueBase(); | ||||
auto *VD = const_cast<ValueDecl *>(Base.dyn_cast<const ValueDecl *>()); | ||||
if (auto *VarD = dyn_cast<VarDecl>(VD)) { | ||||
clang::IntegerLiteral *IL = | ||||
clang::IntegerLiteral::Create(Info.Ctx, llvm::APSInt(32, exp), | ||||
Info.Ctx.IntTy, clang::SourceLocation()); | ||||
VarD->setInit(IL); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See use of llvm-project/clang/lib/AST/ExprConstant.cpp Line 13071 in b2d7d72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really sure what I should be looking at here. Are you objecting to the way StoreExponent is written? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sorry for the late response. Yes, I am objecting to the way There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||
} else { | ||||
llvm_unreachable("expecting a VarDecl for an exponent"); | ||||
} | ||||
} | ||||
|
||||
bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { | ||||
if (!IsConstantEvaluatedBuiltinCall(E)) | ||||
|
@@ -14784,6 +14809,33 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { | |||
default: | ||||
return false; | ||||
|
||||
case Builtin::BIfrexp: | ||||
case Builtin::BIfrexpf: | ||||
case Builtin::BIfrexpl: | ||||
case Builtin::BI__builtin_frexp: | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The non- Some more thought is needed on how to handle the non- There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the non-`_builtin prefixed cases. |
||||
case Builtin::BI__builtin_frexpf: | ||||
tbaederr marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
case Builtin::BI__builtin_frexpl: | ||||
case Builtin::BI__builtin_frexpf16: | ||||
case Builtin::BI__builtin_frexpf128: { | ||||
const auto *FDecl = E->getDirectCallee(); | ||||
Builtin::Context BTC = Info.Ctx.BuiltinInfo; | ||||
if (BTC.isBuiltinConstant(FDecl->getBuiltinID()) > | ||||
Info.Ctx.getLangOpts().LangStd) | ||||
return false; | ||||
LValue Pointer; | ||||
if (!EvaluateFloat(E->getArg(0), Result, Info) || | ||||
!EvaluatePointer(E->getArg(1), Pointer, Info)) | ||||
return false; | ||||
int FrexpExp; | ||||
llvm::RoundingMode RM = getActiveRoundingMode(Info, E); | ||||
Result = llvm::frexp(Result, FrexpExp, RM); | ||||
assert((Result.isZero() || Result.isNaN() || Result.isInfinity() || | ||||
isInFrexpResultRange(Result)) && | ||||
"The value is not in the expected range for frexp."); | ||||
StoreExponent(Pointer, FrexpExp); | ||||
return true; | ||||
} | ||||
|
||||
case Builtin::BI__builtin_huge_val: | ||||
case Builtin::BI__builtin_huge_valf: | ||||
case Builtin::BI__builtin_huge_vall: | ||||
|
@@ -14857,12 +14909,20 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { | |||
return true; | ||||
} | ||||
|
||||
case Builtin::BIfmax: | ||||
case Builtin::BIfmaxf: | ||||
case Builtin::BIfmaxl: | ||||
case Builtin::BI__builtin_fmax: | ||||
case Builtin::BI__builtin_fmaxf: | ||||
case Builtin::BI__builtin_fmaxl: | ||||
case Builtin::BI__builtin_fmaxf16: | ||||
case Builtin::BI__builtin_fmaxf128: { | ||||
// TODO: Handle sNaN. | ||||
const auto *FDecl = E->getDirectCallee(); | ||||
Builtin::Context BTC = Info.Ctx.BuiltinInfo; | ||||
if (BTC.isBuiltinConstant(FDecl->getBuiltinID()) > | ||||
Info.Ctx.getLangOpts().LangStd) | ||||
return false; | ||||
APFloat RHS(0.); | ||||
if (!EvaluateFloat(E->getArg(0), Result, Info) || | ||||
!EvaluateFloat(E->getArg(1), RHS, Info)) | ||||
|
@@ -14875,12 +14935,20 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { | |||
return true; | ||||
} | ||||
|
||||
case Builtin::BIfmin: | ||||
case Builtin::BIfminf: | ||||
Comment on lines
+15093
to
+15094
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't make these evaluate in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These have to be made
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we'll be missing at least "pedantic" diagnostics if we don't restrict the The interaction with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the Clang C/C++ Language Workgroup call on 2024-05-15, it was agreed that For me, the first expectation for this PR (on that subject) is an update to the documentation of @AaronBallman @ldionne, I am no sure how we want to handle this w.r.t. documenting the C++23 implementation status. Does it go in both the Clang documentation (for the functions shared with C) and the libc++ documentation (for the overloads added by C++)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think anything should happen to the libc++ documentation in this patch. We can update the implementation to behave the same across the different overloads in a follow-up and update our documentation then. |
||||
case Builtin::BIfminl: | ||||
case Builtin::BI__builtin_fmin: | ||||
case Builtin::BI__builtin_fminf: | ||||
case Builtin::BI__builtin_fminl: | ||||
case Builtin::BI__builtin_fminf16: | ||||
case Builtin::BI__builtin_fminf128: { | ||||
// TODO: Handle sNaN. | ||||
const auto *FDecl = E->getDirectCallee(); | ||||
Builtin::Context BTC = Info.Ctx.BuiltinInfo; | ||||
if (BTC.isBuiltinConstant(FDecl->getBuiltinID()) > | ||||
Info.Ctx.getLangOpts().LangStd) | ||||
return false; | ||||
APFloat RHS(0.); | ||||
if (!EvaluateFloat(E->getArg(0), Result, Info) || | ||||
!EvaluateFloat(E->getArg(1), RHS, Info)) | ||||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,106 @@ | ||||||||||
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 | ||||||||||
|
||||||||||
// RUN: %clang_cc1 -x c++ -triple x86_64-unknown-unknown -std=c++23 \ | ||||||||||
// RUN: -emit-llvm -o - %s | FileCheck %s | ||||||||||
|
||||||||||
#define NAN (__builtin_nanf("")) | ||||||||||
#define INFINITY (__builtin_inff()) | ||||||||||
|
||||||||||
// CHECK-LABEL: define dso_local noundef i32 @_Z4funcv( | ||||||||||
// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { | ||||||||||
// CHECK-NEXT: entry: | ||||||||||
// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 | ||||||||||
// CHECK-NEXT: [[MIN1:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[MIN2:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[MIN3:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[MIN4:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[MIN5:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[MIN6:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[MIN7:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[MIN8:%.*]] = alloca x86_fp80, align 16 | ||||||||||
// CHECK-NEXT: [[MAX1:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[MAX2:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[MAX3:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[MAX4:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[MAX5:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[MAX6:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[MAX7:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[MAX8:%.*]] = alloca x86_fp80, align 16 | ||||||||||
// CHECK-NEXT: [[FREXP1:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[FREXP2:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[FREXP3:%.*]] = alloca double, align 8 | ||||||||||
// CHECK-NEXT: [[FREXP4:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[FREXP5:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[FREXP6:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[FREXP7:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[FREXP8:%.*]] = alloca x86_fp80, align 16 | ||||||||||
// CHECK-NEXT: [[FREXP9:%.*]] = alloca float, align 4 | ||||||||||
// CHECK-NEXT: [[FREXP10:%.*]] = alloca x86_fp80, align 16 | ||||||||||
// CHECK-NEXT: store i32 0, ptr [[I]], align 4 | ||||||||||
// CHECK-NEXT: store double 1.300000e+00, ptr [[MIN1]], align 8 | ||||||||||
// CHECK-NEXT: store double -0.000000e+00, ptr [[MIN2]], align 8 | ||||||||||
// CHECK-NEXT: store double -0.000000e+00, ptr [[MIN3]], align 8 | ||||||||||
// CHECK-NEXT: store float 0x7FF8000000000000, ptr [[MIN4]], align 4 | ||||||||||
// CHECK-NEXT: store float -1.000000e+00, ptr [[MIN5]], align 4 | ||||||||||
// CHECK-NEXT: store float 0xFFF0000000000000, ptr [[MIN6]], align 4 | ||||||||||
// CHECK-NEXT: store float 0.000000e+00, ptr [[MIN7]], align 4 | ||||||||||
// CHECK-NEXT: store x86_fp80 0xK4005F6E978D4FDF3B646, ptr [[MIN8]], align 16 | ||||||||||
// CHECK-NEXT: store double 1.524000e+01, ptr [[MAX1]], align 8 | ||||||||||
// CHECK-NEXT: store double 0.000000e+00, ptr [[MAX2]], align 8 | ||||||||||
// CHECK-NEXT: store double 0.000000e+00, ptr [[MAX3]], align 8 | ||||||||||
// CHECK-NEXT: store float -1.000000e+00, ptr [[MAX4]], align 4 | ||||||||||
// CHECK-NEXT: store float 0x7FF0000000000000, ptr [[MAX5]], align 4 | ||||||||||
// CHECK-NEXT: store float 0.000000e+00, ptr [[MAX6]], align 4 | ||||||||||
// CHECK-NEXT: store float 0x7FF8000000000000, ptr [[MAX7]], align 4 | ||||||||||
// CHECK-NEXT: store x86_fp80 0xK4008C540C49BA5E353F8, ptr [[MAX8]], align 16 | ||||||||||
// CHECK-NEXT: store double 0x3FEEDCCCCCCCCCCD, ptr [[FREXP1]], align 8 | ||||||||||
// CHECK-NEXT: store double 0.000000e+00, ptr [[FREXP2]], align 8 | ||||||||||
// CHECK-NEXT: store double -0.000000e+00, ptr [[FREXP3]], align 8 | ||||||||||
// CHECK-NEXT: store float 0x7FF8000000000000, ptr [[FREXP4]], align 4 | ||||||||||
// CHECK-NEXT: store float 0xFFF8000000000000, ptr [[FREXP5]], align 4 | ||||||||||
// CHECK-NEXT: store float 0x7FF0000000000000, ptr [[FREXP6]], align 4 | ||||||||||
// CHECK-NEXT: store float 0xFFF0000000000000, ptr [[FREXP7]], align 4 | ||||||||||
// CHECK-NEXT: store x86_fp80 0xK3FFE81A9FBE76C8B4396, ptr [[FREXP8]], align 16 | ||||||||||
// CHECK-NEXT: store float 8.750000e-01, ptr [[FREXP9]], align 4 | ||||||||||
// CHECK-NEXT: store x86_fp80 0xK3FFEEAC7AE147AE14800, ptr [[FREXP10]], align 16 | ||||||||||
// CHECK-NEXT: ret i32 0 | ||||||||||
// | ||||||||||
int func() | ||||||||||
{ | ||||||||||
int i; | ||||||||||
|
||||||||||
// fmin | ||||||||||
hubert-reinterpretcast marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
constexpr double min1 = __builtin_fmin(15.24, 1.3); | ||||||||||
constexpr double min2 = __builtin_fmin(-0.0, +0.0); | ||||||||||
constexpr double min3 = __builtin_fmin(+0.0, -0.0); | ||||||||||
constexpr float min4 = __builtin_fminf(NAN, NAN); | ||||||||||
constexpr float min5 = __builtin_fminf(NAN, -1); | ||||||||||
constexpr float min6 = __builtin_fminf(-INFINITY, 0); | ||||||||||
constexpr float min7 = __builtin_fminf(INFINITY, 0); | ||||||||||
constexpr long double min8 = __builtin_fminl(123.456L, 789.012L); | ||||||||||
|
||||||||||
// fmax | ||||||||||
constexpr double max1 = __builtin_fmax(15.24, 1.3); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Formatting nit:
Suggested change
|
||||||||||
constexpr double max2 = __builtin_fmax(-0.0, +0.0); | ||||||||||
constexpr double max3 = __builtin_fmax(+0.0, -0.0); | ||||||||||
constexpr float max4 = __builtin_fmaxf(NAN, -1); | ||||||||||
constexpr float max5 = __builtin_fmaxf(+INFINITY, 0); | ||||||||||
constexpr float max6 = __builtin_fmaxf(-INFINITY, 0); | ||||||||||
constexpr float max7 = __builtin_fmaxf(NAN, NAN); | ||||||||||
constexpr long double max8 = __builtin_fmaxl(123.456L, 789.012L); | ||||||||||
|
||||||||||
// frexp | ||||||||||
constexpr double frexp1 = __builtin_frexp(123.45, &i); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zahiraam, to be explicit, these tests (as-is) will fail once the comment re: This will probably work:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||
constexpr double frexp2 = __builtin_frexp(0.0, &i); | ||||||||||
constexpr double frexp3 = __builtin_frexp(-0.0, &i); | ||||||||||
constexpr float frexp4 = __builtin_frexpf(NAN, &i); | ||||||||||
constexpr float frexp5 = __builtin_frexpf(-NAN, &i); | ||||||||||
constexpr float frexp6 = __builtin_frexpf(+INFINITY, &i); | ||||||||||
constexpr float frexp7 = __builtin_frexpf(-INFINITY, &i); | ||||||||||
constexpr long double frexp8 = __builtin_frexpl(259.328L, &i); | ||||||||||
constexpr float frexp9 = __builtin_frexpf16(3.5, &i); | ||||||||||
constexpr long double frexp10 = __builtin_frexpf128(234.78, &i); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||
|
||||||||||
return 0; | ||||||||||
} | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
frexp
family of functions cannot be markedconst
because they modify the object pointed to by the pointer parameter.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.