|
29 | 29 | #define itkMath_h |
30 | 30 |
|
31 | 31 | #include <cassert> |
| 32 | +#include <complex> |
32 | 33 | #include <cmath> |
| 34 | +#include <type_traits> |
33 | 35 | #include "itkMathDetail.h" |
34 | 36 | #include "itkConceptChecking.h" |
35 | 37 | #include <vnl/vnl_math.h> |
@@ -823,60 +825,104 @@ using vnl_math::cube; |
823 | 825 | using vnl_math::squared_magnitude; |
824 | 826 |
|
825 | 827 |
|
826 | | -/*============================================ |
827 | | -Decouple dependence and exposure of vnl_math::abs operations |
828 | | -in ITK. Placing this small amount of duplicate vnl_math |
829 | | -code directly in ITK removes backward compatibility |
830 | | -issues with system installed VXL versions. |
831 | | -*/ |
832 | | -inline bool |
833 | | -abs(bool x) |
834 | | -{ |
835 | | - return x; |
836 | | -} |
837 | | -inline unsigned char |
838 | | -abs(unsigned char x) |
839 | | -{ |
840 | | - return x; |
841 | | -} |
842 | | -inline unsigned char |
843 | | -abs(signed char x) |
844 | | -{ |
845 | | - return x < 0 ? static_cast<unsigned char>(-x) : x; |
846 | | -} |
847 | | -inline unsigned char |
848 | | -abs(char x) |
849 | | -{ |
850 | | - return static_cast<unsigned char>(x); |
851 | | -} |
852 | | -inline unsigned short |
853 | | -abs(short x) |
854 | | -{ |
855 | | - return x < 0 ? static_cast<unsigned short>(-x) : x; |
856 | | -} |
857 | | -inline unsigned short |
858 | | -abs(unsigned short x) |
859 | | -{ |
860 | | - return x; |
861 | | -} |
862 | | -inline unsigned int |
863 | | -abs(unsigned int x) |
| 828 | +/** |
| 829 | + * @brief Returns the absolute value of a number. |
| 830 | + * |
| 831 | + * This function provides a c++17 implementation of the vnl_math::abs absolute value functionality. |
| 832 | + * safe_abs preserves backward compatibility with vnl_math::abs that was originally used in ITK. |
| 833 | + * |
| 834 | + * Where std::abs returns the absolute value in the same type as the input, itk::safe_abs |
| 835 | + * returns the absolute value in the unsigned equivalent of the input type. |
| 836 | + * |
| 837 | + * @tparam T The type of the input number. |
| 838 | + * @param x The input number. |
| 839 | + * @return The absolute value of the input number as an unsigned type for integer types. |
| 840 | + */ |
| 841 | +template <typename T> |
| 842 | +constexpr auto |
| 843 | +safe_abs(T x) noexcept |
864 | 844 | { |
865 | | - return x; |
| 845 | + if constexpr (std::is_same_v<T, bool>) |
| 846 | + { |
| 847 | + // std::abs does not provide abs for bool, but ITK depends on bool support for abs. |
| 848 | + return x; |
| 849 | + } |
| 850 | + else if constexpr (std::is_integral_v<T>) |
| 851 | + { |
| 852 | + if constexpr (std::is_signed_v<T>) |
| 853 | + { |
| 854 | + // Using promotion to std::int32_t instead of std::make_unsigned_t<T> |
| 855 | + // to avoid potential overflow when T is a signed 8bit minimum (value -128) |
| 856 | + // or signed 15 bit minimum (value -32768). |
| 857 | + // Note that -(-128) is still -128 for an 8-bit signed char. |
| 858 | + if constexpr (sizeof(T) <= sizeof(int)) |
| 859 | + { |
| 860 | +#if __cplusplus >= 202302L |
| 861 | + return static_cast<std::make_unsigned_t<T>>(std::abs(static_cast<int>(x))); |
| 862 | +#else |
| 863 | + return static_cast<std::make_unsigned_t<T>>((x < T(0)) ? -x : x); |
| 864 | +#endif |
| 865 | + } |
| 866 | + else if constexpr (sizeof(T) <= sizeof(long int)) |
| 867 | + { |
| 868 | +#if __cplusplus >= 202302L |
| 869 | + return static_cast<std::make_unsigned_t<T>>(std::abs(static_cast<long int>(x))); |
| 870 | +#else |
| 871 | + return static_cast<std::make_unsigned_t<T>>((x < T(0)) ? -x : x); |
| 872 | +#endif |
| 873 | + } |
| 874 | + else if constexpr (sizeof(T) == sizeof(long long int)) |
| 875 | + { |
| 876 | + // NOTE: overflow is not resolved if LONG_LONG_INT_MIN value is converted |
| 877 | +#if __cplusplus >= 202302L |
| 878 | + return static_cast<std::make_unsigned_t<T>>(std::abs(static_cast<long long int>(x))); |
| 879 | +#else |
| 880 | + return static_cast<std::make_unsigned_t<T>>((x < T(0)) ? -x : x); |
| 881 | +#endif |
| 882 | + } |
| 883 | + } |
| 884 | + else |
| 885 | + { // In C++17, the std::abs() integer overloads are only for : int, long, and long long. |
| 886 | + // unsigned type values std::abs() are supported through implicit type conversions to |
| 887 | + // the next larger sized integer type. The 'unsigned long long' failed due to an |
| 888 | + // lack of larger sized signed integer type to be promoted to. |
| 889 | + // type of x | default std::abs() called |
| 890 | + // uint8_t | std::abs( (int32_t) x) |
| 891 | + // uint16_t | std::abs( (int32_t) x) |
| 892 | + // uint32_t | std::abs( (int64_t) x) |
| 893 | + // uint64_t | // compiler error |
| 894 | + // This explicit override provides direct support for all unsigned integer types with type promotion. |
| 895 | + return x; |
| 896 | + } |
| 897 | + } |
| 898 | + else if constexpr (std::is_floating_point_v<T>) // floating point or complex<> types |
| 899 | + { // floating point std::abs is constexpr in c++23 and later |
| 900 | +#if __cplusplus >= 202302L |
| 901 | + return std::abs(x); |
| 902 | +#else |
| 903 | + return (x < 0) ? -x : x; |
| 904 | +#endif |
| 905 | + } |
866 | 906 | } |
867 | | -inline unsigned long |
868 | | -abs(unsigned long x) |
| 907 | +template <typename T> |
| 908 | +auto |
| 909 | +safe_abs(const std::complex<T> & x) noexcept |
869 | 910 | { |
870 | | - return x; |
| 911 | + // std::abs<T>(std::complex<T>) is not constexpr in in any proposed c++ standard |
| 912 | + return std::abs<T>(x); |
871 | 913 | } |
872 | | -// long long - target type will have width of at least 64 bits. (since C++11) |
873 | | -inline unsigned long long |
874 | | -abs(unsigned long long x) |
| 914 | + |
| 915 | + |
| 916 | +// itk::Math::abs has different behavior than std::abs. The use of |
| 917 | +// abs() in the ITK context without namespace resolution can be confusing |
| 918 | +// The abs() version is provided for backwards compatibility. |
| 919 | +template <typename T> |
| 920 | +auto |
| 921 | +abs(T x) noexcept |
875 | 922 | { |
876 | | - return x; |
| 923 | + return safe_abs(x); |
877 | 924 | } |
878 | 925 |
|
879 | | -using std::abs; |
880 | 926 |
|
881 | 927 | } // namespace itk::Math |
882 | 928 |
|
|
0 commit comments