Added log pow and fixed exp - #349
Conversation
There was a problem hiding this comment.
Pull request overview
This PR extends the cxp:: constexpr math utilities by adding log/pow support and adjusting the exp implementation, with accompanying unit tests to validate runtime vs constexpr behavior.
Changes:
- Added new
cxp::log/cxp::logfandcxp::pow/cxp::powfAPIs inconstexpr_cmath.h. - Refactored
expinto a templatedcxp::expplus a float wrappercxp::expf, and adjusted numeric-limits handling for CUDA builds. - Expanded utests to include sweeps/edge cases for
expf, and compile-time + runtime tests forlogandpow.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
include/fused_kernel/core/constexpr_libs/constexpr_cmath.h |
Adds log/pow, refactors exp, and adjusts CUDA numeric_limits plumbing. |
utests/core/constexpr_libs/utest_constexpr_expf_exact.h |
Adds runtime/constexpr sweep checks for cxp::expf vs std::expf. |
utests/core/constexpr_libs/utest_constexpr_cmath.h |
Adds compile-time and runtime validation for new log/pow APIs. |
.github/copilot-instructions.md |
Documents guidance about not declaring CUDA compilation blocked based on ad hoc nvcc checks. |
Suppressed comments (1)
include/fused_kernel/core/constexpr_libs/constexpr_cmath.h:1044
- Same shadowing issue in powf::BaseFunc::exec: the parameter name
basepreventsbase::is_constant_evaluated()from compiling. Rename the parameter and keepcxp::basenamespace accesses unshadowed.
using InstanceType = fk::BinaryType;
FK_HOST_DEVICE_FUSE float exec(const float base, const float exponent) {
if (base::is_constant_evaluated()) {
return pow::BaseFunc::exec(base, exponent);
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| using InstanceType = fk::BinaryType; | ||
| template <std::floating_point ST> | ||
| FK_HOST_DEVICE_FUSE ST exec(const ST base, const ST exponent) { | ||
| if (base::is_constant_evaluated()) { | ||
| return c_pow(base, exponent); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
include/fused_kernel/core/constexpr_libs/constexpr_cmath.h:954
pow::BaseFunc::execnames its first parameterbase, which shadows thecxp::basenamespace. As written,base::is_constant_evaluated()will not compile (attempts to use::on a function parameter). Rename the parameter or fully-qualify the namespace (e.g.cxp::base::is_constant_evaluated()).
FK_HOST_DEVICE_FUSE ST exec(const ST base, const ST exponent) {
if (base::is_constant_evaluated()) {
return c_pow(base, exponent);
} else {
return std::pow(base, exponent);
include/fused_kernel/core/constexpr_libs/constexpr_cmath.h:907
log::BaseFunc::c_log(double)similarly assumes a normal double when extracting exponent/mantissa. Subnormal doubles (exponent bits == 0, nonzero mantissa) will produce an incorrect result. Consider normalizing (e.g. scale by 2^54, adjust exponent) before computingeandm.
ulonglong bits = bit_cast<ulonglong>(x);
int e = static_cast<int>(bits >> 52) - 1023;
double m = bit_cast<double>((bits & 0x000FFFFFFFFFFFFFull) | 0x3FF0000000000000ull);
include/fused_kernel/core/constexpr_libs/constexpr_cmath.h:875
log::BaseFunc::c_log(float)extracts the exponent assuming a normal float. For subnormal inputs (exponent field == 0 but mantissa != 0),e/mare computed incorrectly, so constexprcxp::log::f(x)will be wrong for subnormals. Add a normalization step similar toldexpf(scale by 2^24, adjust exponent) before computingeandm.
This issue also appears on line 904 of the same file.
uint bits = bit_cast<uint>(x);
int e = static_cast<int>((bits >> 23) & 0xFF) - 127;
float m = bit_cast<float>((bits & 0x007FFFFF) | 0x3F800000);
if (m > 1.41421356237f) {
| FK_HOST_DEVICE_FUSE float exec(const float base, const float exponent) { | ||
| if (base::is_constant_evaluated()) { | ||
| return pow::BaseFunc::exec(base, exponent); | ||
| } else { | ||
| return std::pow(base, exponent); |
| if (x < -709.0) | ||
| return 0.0; | ||
| if (x > 709.0) | ||
| return base::numeric_limits<double>::infinity(); |
| uint bits = bit_cast<uint>(x); | ||
| int e = static_cast<int>((bits >> 23) & 0xFF) - 127; | ||
|
|
||
| float m = bit_cast<float>((bits & 0x007FFFFF) | 0x3F800000); |
| if (base == 0.0f) | ||
| return (exponent > 0.0f) ? 0.0f : base::numeric_limits<float>::infinity(); | ||
|
|
||
| if (base < 0.0f) { | ||
| int e_int = static_cast<int>(exponent); | ||
| if (static_cast<float>(e_int) == exponent) { | ||
| float res = exp::BaseFunc::exec(exponent * log::BaseFunc::exec(-base)); | ||
| return (e_int % 2 != 0) ? -res : res; | ||
| } | ||
| return base::numeric_limits<float>::quiet_NaN(); |
| #define CXP_F_FUNC \ | ||
| template <typename... Types> \ | ||
| FK_HOST_DEVICE_FUSE auto f(const Types... vals) { \ | ||
| FK_HOST_DEVICE_FUSE decltype(auto) f(const Types... vals) { \ |
| template <std::floating_point ST> | ||
| FK_HOST_DEVICE_FUSE ST exec(const ST x) { |
No description provided.