-
Notifications
You must be signed in to change notification settings - Fork 10
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
Implements rint #58
Implements rint #58
Conversation
} | ||
|
||
template <class Integer, std::enable_if_t<std::is_integral_v<Integer>, bool> = true> | ||
constexpr double rint(Integer num) |
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.
Add noexcept? Also need to document each function.
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.
Following the defined API of the standard there should not be a noexcept
for ccm::rint. This may change later in the future but for the time being aim to mimic the API pretty closely.
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.
You can also use this as a reference:
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.
@Rinzii Sounds good. I removed the noexcept for that method!
I think it is ready for a review!
Glad to see a new PR! I will warn you though. I've recently pushed a major internal restructure of the project to the |
a19e9e7
to
719e2dd
Compare
test/nearest/rint_test.cpp
Outdated
}; | ||
|
||
template <typename T> | ||
std::vector<T> makeTestParams() |
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.
After pushing this all up, I realized I had a question: Is there a specific casing I should be following? I am assuming snake case to match the standard library?
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.
There is no hard rule, but generally yes I follow pretty close to LLVMs style using snake case. You can also use clang-format to aid you in making sure your following the formatting style of the project!
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.
Sounds good, thanks. I set up the clang format in VSCode to run on every save, however it didn't seem to adjust for the snake vs camel case. I will update the casing for this commit! :)
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.
Thank you for taking the time to contribute to CCMath! The work that people like you do is invaluable and I greatly appreciate the time you have put in.
Overall, this is a good start! I have a few minor change requests, but also some major change requests that may be trickier to deal with. If you need help or advice on some of the requests I've made feel free to ping me.
I look forward to your changes!
test/nearest/rint_test.cpp
Outdated
#define CLANG_LINUX | ||
#endif | ||
#endif | ||
|
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.
Can we also test here that in the events where a fenv error is raised that it is actually being set for run time evaluation? (I should also let you know that the internal helpers we use for fenv will not set fenv if we are being evaluated during compile time (though this may change in the future))
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.
Also I know I've yet to start pushing this onto the test cases yet (but I have been planning for it) but now that many functions are starting to account for rounding modes could we also test that our values are correct when the rounding modes change? If this is too much work I understand, but I'd like to start pushing the code base to properly respect and test rounding modes if possible! (Also you should not need to test this for compile time as during compile time the rounding mode is always round-to-nearest!)
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.
Looking at your other comments, I think I did make an oversight on the rounding modes. I was really only considering the compile time evaluation when I wrote them, so thanks for bringing this up!
I think my plan moving forward for this PR is to sometime this week / weekend, I will write tests that run for each rounding mode and then adapt what I currently have to work for the tests. I might be able to simply use your helper functions for the majority of these besides the rounding functions for long and long long types.
include/ccmath/math/nearest/rint.hpp
Outdated
constexpr T rint(T num) | ||
{ | ||
if (ccm::isinf(num) || ccm::isnan(num)) { ccm::support::fenv::raise_except_if_required(FE_INVALID); } | ||
constexpr auto rounding_mode{ccm::support::fenv::get_rounding_mode()}; |
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.
This function can't have rounding_mode
as constexpr
because if the compiler evaluates get_rounding_mode()
at compile time, it will lock in a specific rounding mode (e.g., round-to-nearest) regardless of the actual rounding mode at runtime. This would lead to incorrect rounding behavior when the function is called at runtime since the rounding mode may differ from the one determined at compile time. So I'd remove the constexpr
qualifier here.
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.
Of course you can change this to const
to take advantage of constant folding!
*/ | ||
constexpr long lrint(float num) | ||
{ | ||
return static_cast<long>(ccm::rint(num)); |
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.
Ok, this function here is a bit trickier and performing a static_cast
won't actually be enough to respect all rounding modes. static_cast
does not perform conversion with floats but instead performs truncation and this will always follow the default rounding mode. As such if the rounding mode is different than the default this should not work as expected. The proper solution is to instead get the bits of the float itself and perform rounding towards a signed integer using the bits themselve. There is a few manners to do this, but it will be a tricky problem to fix. Let me know your thoughts!
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.
I will have to investigate this after writing the tests I think, cause I initially assumed this would be an OK cast to perform since the long and float are the same size? I will have to brush up on my casting :)
Thanks for pointing this out!
*/ | ||
constexpr long lrint(double num) | ||
{ | ||
if (ccm::isnan(num)) |
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.
This will also suffer from the issue I mentioned earlier with static_cast
along with every other lrint
function that converts a float to a integer.
I noticed two things from your comments that seem the most important. I wanted to write them down here because I don't think I addressed these in an earlier PR for implementing
Should I also re-address these two things there? I think for this PR, I could change Thanks for the review! |
Functions implemented: - ccm::rint(float) - ccm::rint(double) - ccm::rint(long double) - ccm::rintl(long double) - ccm::rintf(float) - ccm::lrint(float) - ccm::lrint(double) - ccm::lrint(long double) - ccm::lrintl(long double) - ccm::lrintf(float) - ccm::llrint(float) - ccm::llrint(double) - ccm::llrint(long double) - ccm::llrintl(long double) - ccm::llrintf(long double) Tests written for: - Common and edge case conditions for `float`, `double`, and `long double` inputs. - Assert that FE_INVALID is raised for +/- infinity values and NaN values. - Asserts that functions can be evaluated at compile time.
Using cmake to generate different test cases is perfectly fine. Also adding a msvc specific return is perfectly fine and actually encouraged as ccmath tends to aim to follow the expected output of each compiler! Also don't worry about taking your time with the PR. I'd rather you took the time to get everything right than rushing stuff! If you have any specific questions for me just lmk! I've been pretty busy these last few weeks, but I'll try to get back to any questions you have promptly. |
Hey @khurd21 sorry for being somewhat away. Been going through quite a bit recently, but I have made a discord specifically for ccmath to allow quicker collaboration between contributors or users of the library. Feel free to join if you so wish! Discord Link: https://discord.gg/p3mVxAbdmc |
Also what is the current status of this PR if I may ask? |
Hey! Sorry I haven't touched this in a long time! The current status is I believe I need to update the code slightly to be compatible with how MSVC handles the edge cases compared to other compilers. I also wanted to double check that the test cases were accurate |
Hey @khurd21 I wanted to do one last check in. Is there still plans to continue this PR or should I close it? |
Closing due to being stale. |
Functions implemented:
ccm::rint(float)
ccm::rint(double)
ccm::rint(long double)
ccm::rintl(long double)
ccm::rintf(float)
ccm::lrint(float)
ccm::lrint(double)
ccm::lrint(long double)
ccm::lrintl(long double)
ccm::lrintf(float)
ccm::llrint(float)
ccm::llrint(double)
ccm::llrint(long double)
ccm::llrintl(long double)
ccm::llrintf(long double)
Tests written for:
float
,double
, andlong double
inputs.values.