-
Notifications
You must be signed in to change notification settings - Fork 1.6k
<complex>: Improve numerical accuracy of sqrt and log #935
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
Merged
Stephan T. Lavavej (StephanTLavavej)
merged 24 commits into
microsoft:master
from
statementreply:improve_complex_sqrt_log
Nov 9, 2020
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
903c59f
Fix undue overflow and underflow in complex sqrt
statementreply a6a5934
Improve accuracy of `log` when |z| is close to 1
statementreply 7843118
Minor fixes, clarify comments
statementreply 1d34722
Add complex sqrt test
statementreply d79c9f3
Add complex log test
statementreply b33c29e
Remove internal header file from CMake source file list
statementreply e259009
Add new file to MSBuild project
statementreply ccf5cdc
Fix log(complex{1, tiny}) incorrectly returning -0 under FE_DOWNWARD
statementreply 5bcece4
Use hardware FMA on arm64
statementreply dd608bf
Merge branch 'master' into improve_complex_sqrt_log
statementreply 09e805a
Fix shadowing
statementreply d1e2ab8
Add comments
statementreply 0b281c7
Fix calling nonexistent ::signbit
statementreply 5853238
Fix comments
statementreply ad16ad0
Code review comments
statementreply be1431b
Remove PM_CLANG
statementreply 434f604
Merge branch 'master' into gh935_complex
StephanTLavavej 792d0bb
Merge branch 'master' into gh935_complex
StephanTLavavej 2b39e7b
Code review feedback.
StephanTLavavej 984c8a4
Add -Wno-unused-command-line-argument for internal tests
StephanTLavavej 54801c9
Merge branch 'master' into gh935
StephanTLavavej 6c37763
Merge branch 'master' into gh935
StephanTLavavej 5a089e9
Use header-only code to fix /clr:pure.
StephanTLavavej ba88342
Include arm64_neon.h, no arm64 subdirectory.
StephanTLavavej File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| #pragma once | ||
|
|
||
| #ifdef FP_CONFIG_PRESET | ||
| #if FP_CONFIG_PRESET == 3 | ||
| #define FP_PRESET_FAST 1 | ||
| #else // ^^^ FP_CONFIG_PRESET == 3 / FP_CONFIG_PRESET != 3 vvv | ||
| #define FP_PRESET_FAST 0 | ||
| #endif // ^^^ FP_CONFIG_PRESET != 3 ^^^ | ||
| #endif // defined(FP_CONFIG_PRESET) | ||
|
|
||
| #ifdef FP_CONTRACT_MODE | ||
| #ifdef __clang__ | ||
|
|
||
| #if FP_CONTRACT_MODE == 0 | ||
| #pragma STDC FP_CONTRACT OFF | ||
| #elif FP_CONTRACT_MODE == 1 // ^^^ no floating point contraction / standard floating point contraction vvv | ||
| #pragma STDC FP_CONTRACT ON | ||
| #elif FP_CONTRACT_MODE == 2 // ^^^ standard floating point contraction / fast floating point contraction vvv | ||
| #pragma STDC FP_CONTRACT ON | ||
| #else // ^^^ fast floating point contraction / invalid FP_CONTRACT_MODE vvv | ||
| #error invalid FP_CONTRACT_MODE | ||
| #endif // ^^^ invalid FP_CONTRACT_MODE ^^^ | ||
|
|
||
| #else // ^^^ clang / MSVC vvv | ||
|
|
||
| #if FP_CONTRACT_MODE == 0 | ||
| #pragma fp_contract(off) | ||
| #elif FP_CONTRACT_MODE == 1 // ^^^ no floating point contraction / standard floating point contraction vvv | ||
| #pragma fp_contract(on) | ||
| #elif FP_CONTRACT_MODE == 2 // ^^^ standard floating point contraction / fast floating point contraction vvv | ||
| #pragma fp_contract(on) | ||
| #else // ^^^ fast floating point contraction / invalid FP_CONTRACT_MODE vvv | ||
| #error invalid FP_CONTRACT_MODE | ||
| #endif // ^^^ invalid FP_CONTRACT_MODE ^^^ | ||
|
|
||
| #endif // ^^^ MSVC ^^^ | ||
| #endif // defined(FP_CONTRACT_MODE) | ||
|
|
||
| #include <cassert> | ||
| #include <float.h> | ||
|
|
||
| struct fenv_initializer_t { | ||
| fenv_initializer_t() { | ||
| #if WITH_FP_ABRUPT_UNDERFLOW | ||
| { | ||
| const errno_t result = _controlfp_s(nullptr, _DN_FLUSH, _MCW_DN); | ||
| assert(result == 0); | ||
| } | ||
| #endif // WITH_FP_ABRUPT_UNDERFLOW | ||
| } | ||
|
|
||
| ~fenv_initializer_t() = default; | ||
|
|
||
| fenv_initializer_t(const fenv_initializer_t&) = delete; | ||
| fenv_initializer_t& operator=(const fenv_initializer_t&) = delete; | ||
| }; | ||
|
|
||
| const fenv_initializer_t fenv_initializer{}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| RUNALL_INCLUDE ..\floating_point_model_matrix.lst | ||
241 changes: 241 additions & 0 deletions
241
tests/std/tests/GH_000935_complex_numerical_accuracy/floating_point_utils.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <cassert> | ||
| #include <cfenv> | ||
| #include <cmath> | ||
| #include <float.h> | ||
| #include <type_traits> | ||
| #include <xutility> | ||
|
|
||
| namespace fputil { | ||
| template <typename T> | ||
| using float_bits_t = typename _STD _Float_traits<T>::type; | ||
|
|
||
| template <typename T> | ||
| _INLINE_VAR constexpr float_bits_t<T> magnitude_mask_v = _STD _Float_traits<T>::_Magnitude_mask; | ||
|
|
||
| template <typename T> | ||
| _INLINE_VAR constexpr float_bits_t<T> exponent_mask_v = _STD _Float_traits<T>::_Exponent_mask; | ||
|
|
||
| template <typename T> | ||
| _INLINE_VAR constexpr float_bits_t<T> significand_mask_v = magnitude_mask_v<T> & ~exponent_mask_v<T>; | ||
|
|
||
| template <typename T> | ||
| _INLINE_VAR constexpr float_bits_t<T> sign_mask_v = _STD _Float_traits<T>::_Sign_mask; | ||
|
|
||
| template <typename T> | ||
| _INLINE_VAR constexpr float_bits_t<T> norm_min_bits_v = significand_mask_v<T> + 1U; | ||
|
|
||
| template <typename T> | ||
| _INLINE_VAR constexpr float_bits_t<T> norm_max_bits_v = exponent_mask_v<T> - 1U; | ||
|
|
||
| template <typename T> | ||
| _INLINE_VAR constexpr float_bits_t<T> infinity_bits_v = exponent_mask_v<T>; | ||
|
|
||
| // not affected by abrupt underflow | ||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| constexpr bool iszero(const T& x) { | ||
| return _STD _Float_abs_bits(x) == 0; | ||
| } | ||
|
|
||
| // not affected by /fp:fast | ||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| constexpr bool signbit(const T& x) { | ||
| const auto bits = std::_Bit_cast<float_bits_t<T>>(x); | ||
| return (bits & sign_mask_v<T>) != 0; | ||
| } | ||
|
|
||
| enum class rounding_mode { | ||
| to_nearest_ties_even = FE_TONEAREST, | ||
| toward_zero = FE_TOWARDZERO, | ||
| toward_positive = FE_UPWARD, | ||
| toward_negative = FE_DOWNWARD, | ||
| }; | ||
|
|
||
| bool is_directed_rounding_mode(const rounding_mode mode) { | ||
| switch (mode) { | ||
| case rounding_mode::to_nearest_ties_even: | ||
| return false; | ||
|
|
||
| case rounding_mode::toward_zero: | ||
| case rounding_mode::toward_positive: | ||
| case rounding_mode::toward_negative: | ||
| return true; | ||
|
|
||
| default: | ||
| assert(false); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| #if TEST_FP_ROUNDING | ||
|
|
||
| #ifdef __clang__ | ||
| // TRANSITION, should be #pragma STDC FENV_ACCESS ON | ||
| #else // ^^^ clang / MSVC vvv | ||
| // TRANSITION, VSO-923474 -- should be #pragma STDC FENV_ACCESS ON | ||
| #pragma fenv_access(on) | ||
| #endif // ^^^ MSVC ^^^ | ||
|
|
||
| constexpr rounding_mode all_rounding_modes[] = { | ||
| rounding_mode::to_nearest_ties_even, | ||
| rounding_mode::toward_zero, | ||
| rounding_mode::toward_positive, | ||
| rounding_mode::toward_negative, | ||
| }; | ||
|
|
||
| class rounding_guard { | ||
| public: | ||
| explicit rounding_guard(const rounding_mode mode) : old_mode{static_cast<rounding_mode>(std::fegetround())} { | ||
| const int result = std::fesetround(static_cast<int>(mode)); | ||
| assert(result == 0); | ||
| } | ||
|
|
||
| ~rounding_guard() { | ||
| const int result = std::fesetround(static_cast<int>(old_mode)); | ||
| assert(result == 0); | ||
| } | ||
|
|
||
| rounding_guard(const rounding_guard&) = delete; | ||
| rounding_guard& operator=(const rounding_guard&) = delete; | ||
|
|
||
| private: | ||
| rounding_mode old_mode; | ||
| }; | ||
|
|
||
| #else // ^^^ alternative rounding modes / default rounding mode only vvv | ||
|
|
||
| constexpr rounding_mode all_rounding_modes[] = {rounding_mode::to_nearest_ties_even}; | ||
|
|
||
| class rounding_guard { | ||
| public: | ||
| explicit rounding_guard(const rounding_mode mode) { | ||
| static_cast<void>(mode); | ||
| } | ||
|
|
||
| ~rounding_guard() = default; | ||
|
|
||
| rounding_guard(const rounding_guard&) = delete; | ||
| rounding_guard& operator=(const rounding_guard&) = delete; | ||
| }; | ||
|
|
||
| #endif // ^^^ default rounding mode only ^^^ | ||
|
|
||
| // compares whether two floating point values are equal | ||
| // all NaNs are equal, +0.0 and -0.0 are not equal | ||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| bool precise_equal(const T& actual, const T& expected) { | ||
| if (_STD _Is_nan(actual) || _STD _Is_nan(expected)) { | ||
| return _STD _Is_nan(actual) == _STD _Is_nan(expected); | ||
| } else { | ||
| return actual == expected && fputil::signbit(actual) == fputil::signbit(expected); | ||
| } | ||
| } | ||
|
|
||
| namespace detail { | ||
| // 0x80...00 = zero, 0x80...01 = numeric_limits<T>::denorm_min(), 0x7f...ff = -numeric_limits<T>::denorm_min() | ||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| float_bits_t<T> offset_representation(const T& x) { | ||
| const float_bits_t<T> abs_bits = _STD _Float_abs_bits(x); | ||
| return fputil::signbit(x) ? sign_mask_v<T> - abs_bits : sign_mask_v<T> + abs_bits; | ||
| } | ||
|
|
||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| float_bits_t<T> is_offset_value_subnormal_or_zero(const float_bits_t<T> offset_value) { | ||
| constexpr float_bits_t<T> positive_norm_min_offset = sign_mask_v<T> + norm_min_bits_v<T>; | ||
| constexpr float_bits_t<T> negative_norm_min_offset = sign_mask_v<T> - norm_min_bits_v<T>; | ||
|
|
||
| return negative_norm_min_offset < offset_value && offset_value < positive_norm_min_offset; | ||
| } | ||
|
|
||
| // number of ulps above zero, if we count [0, numeric_limits<T>::min()) as 1 ulp | ||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| double abrupt_underflow_ulp(const float_bits_t<T> offset_value) { | ||
| using bits_type = float_bits_t<T>; | ||
|
|
||
| constexpr bits_type offset_positive_norm_min = sign_mask_v<T> + norm_min_bits_v<T>; | ||
| constexpr bits_type offset_negative_norm_min = sign_mask_v<T> - norm_min_bits_v<T>; | ||
|
|
||
| if (offset_value >= offset_positive_norm_min) { | ||
| return 1.0 + (offset_value - offset_positive_norm_min); | ||
| } else if (offset_value <= offset_negative_norm_min) { | ||
| return -1.0 - (offset_negative_norm_min - offset_value); | ||
| } else if (offset_value >= sign_mask_v<T>) { | ||
| return static_cast<double>(offset_value - sign_mask_v<T>) / norm_min_bits_v<T>; | ||
| } else { | ||
| return -static_cast<double>(sign_mask_v<T> - offset_value) / norm_min_bits_v<T>; | ||
| } | ||
| } | ||
|
|
||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| bool is_within_ulp_tolerance(const T& actual, const T& expected, const int ulp_tolerance) { | ||
| if (_STD _Is_nan(actual) || _STD _Is_nan(expected)) { | ||
| return _STD _Is_nan(actual) == _STD _Is_nan(expected); | ||
| } | ||
|
|
||
| if (_STD _Is_inf(expected)) { | ||
| return actual == expected; | ||
| } | ||
|
|
||
| if (fputil::signbit(actual) != fputil::signbit(expected)) { | ||
| return false; | ||
| } | ||
|
|
||
| using bits_type = float_bits_t<T>; | ||
|
|
||
| // compute ulp difference | ||
| const bits_type actual_offset = detail::offset_representation(actual); | ||
| const bits_type expected_offset = detail::offset_representation(expected); | ||
| const bits_type ulp_diff = | ||
| actual_offset < expected_offset ? expected_offset - actual_offset : actual_offset - expected_offset; | ||
|
|
||
| if (ulp_diff <= static_cast<unsigned int>(ulp_tolerance) && ulp_tolerance >= 0) { | ||
| return true; | ||
| } | ||
|
|
||
| #if WITH_FP_ABRUPT_UNDERFLOW | ||
| // handle abrupt underflow | ||
| if (detail::is_offset_value_subnormal_or_zero<T>(expected_offset) | ||
| || detail::is_offset_value_subnormal_or_zero<T>(actual_offset)) { | ||
| const double adjusted_actual_ulp = detail::abrupt_underflow_ulp<T>(actual_offset); | ||
| const double adjusted_expected_ulp = detail::abrupt_underflow_ulp<T>(expected_offset); | ||
| const double adjusted_ulp_diff = std::abs(adjusted_actual_ulp - adjusted_expected_ulp); | ||
|
|
||
| if (adjusted_ulp_diff <= ulp_tolerance) { | ||
| return true; | ||
| } | ||
| } | ||
| #endif // WITH_FP_ABRUPT_UNDERFLOW | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| bool is_within_absolute_tolerance(const T& actual, const T& expected, const double absolute_tolerance) { | ||
| return _STD _Is_finite(actual) && _STD _Is_finite(expected) | ||
| && std::abs(actual - expected) <= absolute_tolerance; | ||
| } | ||
| } // namespace detail | ||
|
|
||
| // returns whether floating point result is nearly equal to the expected value | ||
| template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> | ||
| bool near_equal( | ||
| const T& actual, const T& expected, const int ulp_tolerance = 1, const double absolute_tolerance = 0) { | ||
| if (precise_equal(actual, expected)) { | ||
| return true; | ||
| } | ||
|
|
||
| if (ulp_tolerance > 0 && detail::is_within_ulp_tolerance(actual, expected, ulp_tolerance)) { | ||
| return true; | ||
| } | ||
|
|
||
| if (absolute_tolerance > 0 && detail::is_within_absolute_tolerance(actual, expected, absolute_tolerance)) { | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } // namespace fputil |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.