Skip to content

Commit d234458

Browse files
author
peng.li24
committed
fix: float32 unwrap bit-exact match with numpy using floor-based modulo
- Replace round-based ddmod with numpy's exact path: floor((dd+p2)/period)*period - Add boundary_ambiguous handling: when dd>0 and ddmod==-p2, use +p2 - Restore float32 large-value unwrap test coverage (now bit-exact) - All 475 tests pass
1 parent c9382d7 commit d234458

3 files changed

Lines changed: 15 additions & 5 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Build directories
22
build/
33
cmake-build-*/
4+
issue/
45

56
# Compiled output
67
*.o

numpy/core.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,17 +720,26 @@ inline double safe_divide(double a, double b, double default_val = 0.0) {
720720

721721
/// numpy.unwrap(p, discont=None, axis=-1)
722722
/// 1D only: unwrap phase angles by correcting jumps > discont.
723+
/// Uses numpy's exact computation path: (dd+period/2)%period - period/2
724+
/// to guarantee bit-identical float32 results.
723725
template<typename T>
724726
inline void unwrap(const T* src, T* dst, size_t n, T discont = T(M_PI)) {
725727
if (n == 0) return;
726728
T period = T(2) * discont;
729+
T p2 = period / T(2);
727730
dst[0] = src[0];
728731
T cum_correct = T(0);
729732
for (size_t i = 1; i < n; ++i) {
730733
T dd = src[i] - src[i - 1];
731734
T ph_correct = T(0);
732735
if (std::abs(dd) >= discont) {
733-
T ddmod = dd - std::round(dd / period) * period;
736+
// numpy: ddmod = (dd + period/2) % period - period/2
737+
// Python-style mod using floor division (numpy's mod):
738+
T val = dd + p2;
739+
T val_mod = val - std::floor(val / period) * period;
740+
T ddmod = val_mod - p2;
741+
// boundary_ambiguous: when dd > 0 and ddmod == -period/2, use +period/2
742+
if (dd > T(0) && ddmod == -p2) ddmod = p2;
734743
ph_correct = ddmod - dd;
735744
}
736745
cum_correct += ph_correct;

tests/test_all.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -736,10 +736,10 @@ def test_unwrap(cpp):
736736
for dt in [np.float64, np.float32]:
737737
a = np.array([0.0, 0.5, 0.8, -0.9, -0.5, 0.2], dtype=dt)
738738
assert_bit_aligned(cpp.unwrap(a), np.unwrap(a), f"unwrap_{dt}")
739-
# Large values: numpy uses float64 π internally even for float32 input,
740-
# so float32 unwrap is not bit-exact on the correction path. Test float64 only.
741-
a2 = np.array([0.0, 2.5, 5.0, -2.5, -5.0], dtype=np.float64) * np.pi
742-
assert_bit_aligned(cpp.unwrap(a2), np.unwrap(a2), "unwrap_large")
739+
# Large values — bit-exact for both float64 and float32
740+
for dt in [np.float64, np.float32]:
741+
a2 = np.array([0.0, 2.5, 5.0, -2.5, -5.0], dtype=dt) * np.pi
742+
assert_bit_aligned(cpp.unwrap(a2), np.unwrap(a2), f"unwrap_large_{dt.__name__}")
743743

744744
def test_cumsum(cpp):
745745
for dt in [np.float64, np.float32]:

0 commit comments

Comments
 (0)