Skip to content

Implements rint #58

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions include/ccmath/math/nearest/rint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,239 @@

#pragma once

#include <ccmath/internal/support/fenv/fenv_support.hpp>
#include <ccmath/internal/support/fenv/rounding_mode.hpp>
#include <ccmath/internal/support/fp/directional_rounding_utils.hpp>
#include <ccmath/math/compare/isinf.hpp>
#include <ccmath/math/compare/isnan.hpp>
#include <type_traits>

namespace ccm
{
/**
* @brief Rounds the floating-point argument num to an integer value (in floating-point format), using the current rounding mode. The library provides
* overloads of std::rint for all cv-unqualified floating-point types as the type of the parameter num.
* @tparam T The type of the number.
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
template <class T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
constexpr T rint(T num)
{
if (ccm::isinf(num) || ccm::isnan(num)) { ccm::support::fenv::raise_except_if_required(FE_INVALID); }
const auto rounding_mode{ccm::support::fenv::get_rounding_mode()};
return ccm::support::fp::directional_round(num, rounding_mode);
}

/**
* @brief Additional overloads are provided for all integer types, which are treated as double.
* @tparam Integer The type of the integral value.
* @param num An integral value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
template <class Integer, std::enable_if_t<std::is_integral_v<Integer>, bool> = true>
constexpr double rint(Integer num)
Copy link
Contributor Author

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.

Copy link
Owner

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.

Copy link
Owner

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:

https://en.cppreference.com/w/cpp/numeric/math/rint

Copy link
Contributor Author

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!

{
return static_cast<double>(num);
}

/**
* @brief Additional overloads are provided for all integer types, which are treated as double.
* @tparam Integer The type of the integral value.
* @param num An integral value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
template <class Integer, std::enable_if_t<std::is_integral_v<Integer>, bool> = true>
constexpr long lrint(Integer num)
{
return static_cast<long>(num);
}

/**
* @brief Additional overloads are provided for all integer types, which are treated as double.
* @tparam Integer The type of the integral value.
* @param num An integral value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
template <class Integer, std::enable_if_t<std::is_integral_v<Integer>, bool> = true>
constexpr long long llrint(Integer num)
{
return static_cast<long long>(num);
}

/**
* @brief Rounds the floating-point argument num to an integer value (in floating-point format), using the current rounding mode. The library provides
* overloads of std::rint for all cv-unqualified floating-point types as the type of the parameter num.
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr float rint(float num)
{
return ccm::rint<float>(num);
}

/**
* @brief Rounds the floating-point argument num to an integer value (in floating-point format), using the current rounding mode. The library provides
* overloads of std::rint for all cv-unqualified floating-point types as the type of the parameter num.
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr double rint(double num)
{
return ccm::rint<double>(num);
}

/**
* @brief Rounds the floating-point argument num to an integer value (in floating-point format), using the current rounding mode. The library provides
* overloads of std::rint for all cv-unqualified floating-point types as the type of the parameter num.
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long double rint(long double num)
{
return ccm::rint<long double>(num);
}

/**
* @brief Rounds the floating-point argument num to an integer value (in floating-point format), using the current rounding mode. The library provides
* overloads of std::rint for all cv-unqualified floating-point types as the type of the parameter num.
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr float rintf(float num)
{
return ccm::rint<float>(num);
}

/**
* @brief Rounds the floating-point argument num to an integer value (in floating-point format), using the current rounding mode. The library provides
* overloads of std::rint for all cv-unqualified floating-point types as the type of the parameter num.
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long double rintl(long double num)
{
return ccm::rint<long double>(num);
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long lrint(float num)
{
return static_cast<long>(ccm::rint(num));
Copy link
Owner

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!

Copy link
Contributor Author

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!

}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long lrint(double num)
{
if (ccm::isnan(num))
Copy link
Owner

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.

{
ccm::support::fenv::raise_except_if_required(FE_INVALID);
return std::numeric_limits<long>::min();
}
return static_cast<long>(ccm::rint(num));
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long lrint(long double num)
{
return static_cast<long>(ccm::rint(num));
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long lrintf(float num)
{
return static_cast<long>(ccm::rint(num));
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long lrintl(long double num)
{
return static_cast<long>(ccm::rint(num));
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long long llrint(float num)
{
return static_cast<long long>(ccm::rint(num));
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long long llrint(double num)
{
if (ccm::isnan(num))
{
ccm::support::fenv::raise_except_if_required(FE_INVALID);
return std::numeric_limits<long long>::min();
}
return static_cast<long long>(ccm::rint(num));
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long long llrint(long double num)
{
return static_cast<long long>(ccm::rint(num));
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long long llrintf(float num)
{
return static_cast<long long>(ccm::rint(num));
}

/**
* @brief Rounds the floating-point argument num to an integer value, using the current rounding mode. The library provides overloads of std::lrint and
* std::llrint for all cv-unqualified floating-point types as the type of the parameter num.(
* @param num A floating-point value.
* @return If no errors occur, the nearest integer value to num, according to the current rounding mode, is returned.
*/
constexpr long long llrintl(long double num)
{
return static_cast<long long>(ccm::rint(num));
}

} // namespace ccm
23 changes: 22 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,32 @@ target_link_libraries(${PROJECT_NAME}-fmanip PRIVATE
)

add_executable(${PROJECT_NAME}-nearest)
set(ROUNDING_MODES
FE_DOWNWARD
FE_UPWARD
FE_TONEAREST
FE_TOWARDZERO
)
set(GENERATED_SOURCES)
foreach(MODE IN LISTS ROUNDING_MODES)
# Set the output file name based on the rounding mode
string(REPLACE "FE_" "" MODE_SUFFIX ${MODE})
set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/rounding_${MODE_SUFFIX}.cpp")
set(ROUNDING_MODE "FE_${MODE_SUFFIX}")
# Configure the template file to generate the specific cpp file
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/nearest/rint_test.cpp.in
${OUTPUT_FILE}
@ONLY
)
list(APPEND GENERATED_SOURCES ${OUTPUT_FILE})
endforeach()
target_sources(${PROJECT_NAME}-nearest PRIVATE
nearest/floor_test.cpp
nearest/nearbyint_test.cpp
nearest/trunc_test.cpp

nearest/rint_test_compile_time.cpp
${GENERATED_SOURCES}
)
target_link_libraries(${PROJECT_NAME}-nearest PRIVATE
ccmath::test
Expand Down
Loading
Loading