Skip to content

<complex>: Implementation divergence for division by zero #2728

@StephanTLavavej

Description

@StephanTLavavej

Test case: https://godbolt.org/z/WqzW5b99E

#include <complex>
#include <cstdio>
using namespace std;

int main() {
    using C = complex<double>;
    const C t1 = C{1.0} / 0.0;
    const C t2 = C{1.0} / C{0.0};
    printf("    t1: (%g, %g)\n", t1.real(), t1.imag());
    printf("t1 alt: (%g, -%g)\n", t1.real(), -t1.imag());
    printf("    t2: (%g, %g)\n", t2.real(), t2.imag());
}

MSVC has always printed (this is not a regression):

    t1: (inf, -nan(ind))
t1 alt: (inf, -nan)
    t2: (nan, nan)

GCC/libstdc++ and Clang/libc++ print:

    t1: (inf, -nan)
t1 alt: (inf, -nan)
    t2: (inf, -nan)

This was originally reported as DevCom-10040631 and internal VSO-1539658 / AB#1539658 . The user is wondering whether this is a bug.

I've added "t1 alt" to show that "-nan(ind)" is just how MSVC likes to print negative NaN. Otherwise, the behavior for C{1.0} / 0.0 is consistent across implementations, and our implementation is just component-wise, so this is unsurprising:

STL/stl/inc/complex

Lines 1465 to 1471 in 1a20fe1

template <class _Ty>
_NODISCARD _CONSTEXPR20 complex<_Ty> operator/(const complex<_Ty>& _Left, const _Ty& _Right) {
complex<_Ty> _Tmp(_Left);
_Tmp.real(_Tmp.real() / _Right);
_Tmp.imag(_Tmp.imag() / _Right);
return _Tmp;
}

For C{1.0} / C{0.0}, we go through:

STL/stl/inc/complex

Lines 993 to 995 in 1a20fe1

} else if (_Rightimag == 0) { // set NaN result
this->_Val[_RE] = _Myctraits::_Nanv();
this->_Val[_IM] = this->_Val[_RE];

within the _Div() function:

STL/stl/inc/complex

Lines 970 to 1009 in 1a20fe1

template <class _Other>
_CONSTEXPR20 void _Div(const complex<_Other>& _Right) {
using _Myctraits = _Ctraits<_Ty>;
_Ty _Rightreal = static_cast<_Ty>(_Right.real());
_Ty _Rightimag = static_cast<_Ty>(_Right.imag());
if (_Myctraits::_Isnan(_Rightreal) || _Myctraits::_Isnan(_Rightimag)) { // set NaN result
this->_Val[_RE] = _Myctraits::_Nanv();
this->_Val[_IM] = this->_Val[_RE];
} else if ((_Rightimag < 0 ? -_Rightimag : +_Rightimag)
< (_Rightreal < 0 ? -_Rightreal : +_Rightreal)) { // |_Right.imag()| < |_Right.real()|
_Ty _Wr = _Rightimag / _Rightreal;
_Ty _Wd = _Rightreal + _Wr * _Rightimag;
if (_Myctraits::_Isnan(_Wd) || _Wd == 0) { // set NaN result
this->_Val[_RE] = _Myctraits::_Nanv();
this->_Val[_IM] = this->_Val[_RE];
} else { // compute representable result
_Ty _Tmp = (this->_Val[_RE] + this->_Val[_IM] * _Wr) / _Wd;
this->_Val[_IM] = (this->_Val[_IM] - this->_Val[_RE] * _Wr) / _Wd;
this->_Val[_RE] = _Tmp;
}
} else if (_Rightimag == 0) { // set NaN result
this->_Val[_RE] = _Myctraits::_Nanv();
this->_Val[_IM] = this->_Val[_RE];
} else { // 0 < |_Right.real()| <= |_Right.imag()|
_Ty _Wr = _Rightreal / _Rightimag;
_Ty _Wd = _Rightimag + _Wr * _Rightreal;
if (_Myctraits::_Isnan(_Wd) || _Wd == 0) { // set NaN result
this->_Val[_RE] = _Myctraits::_Nanv();
this->_Val[_IM] = this->_Val[_RE];
} else { // compute representable result
_Ty _Tmp = (this->_Val[_RE] * _Wr + this->_Val[_IM]) / _Wd;
this->_Val[_IM] = (this->_Val[_IM] * _Wr - this->_Val[_RE]) / _Wd;
this->_Val[_RE] = _Tmp;
}
}
}

Therefore we're deliberately returning (nan, nan) here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixedSomething works now, yay!high priorityImportant!

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions