How to make SIMD divisions by zero give zero? (x86-64)

170 Views Asked by At

I have floats that I want to divide, some of them may be zeros. How can I make it such that division by zeroes, when they happen, just return zero instead of NaN on x86-64?

I tried setting the FZ and DAZ flags of the MXCSR, to no avail. Am I misunderstanding something? Is Flush to Zero + Denormals Are Zero not supposed to make divisions by zero give zero?

#include <stdio.h>
#include <xmmintrin.h>

int main()
{
    #define CSR_FLUSH_TO_ZERO      (1 << 15)
    #define CSR_DENORMALS_ARE_ZERO (1 << 6)
    unsigned int csr = _mm_getcsr();
    csr |= CSR_FLUSH_TO_ZERO;
    csr |= CSR_DENORMALS_ARE_ZERO;
    _mm_setcsr(csr);

    __m128 a = { 0 };
    __m128 b = { 0 };
    a = _mm_div_ps(a, b);

    float f[4];
    _mm_store_ps(f, a);
    printf("%f\n", f[0]); // prints out 'nan'
}

https://godbolt.org/z/cfzPe5jcG

1

There are 1 best solutions below

3
Peter Cordes On BEST ANSWER

There's no MXCSR setting that will do that, you'll need an extra couple instructions (cmpps against the divisor, and andps or andnps on the result) to mask off the elements where the divisor input was ==0.0f.


Division by zero produces +-infinity, or NaN if the dividend was also zero. There's no subnormal output to Flush To Zero.

Enabling DAZ would make 0 / subnornal treat the subnormal as exactly 0.0f and give you NaN instead of 0.0f with a zero dividend. For a non-zero normalized dividend, you'd still get overflow to +-Infinity with a subnormal divisor.

FTZ (Flush To Zero) only does anything if the result would be subnormal. It disables gradual underflow; that's the only case where flushing happens, not other cases that would raise other FP exceptions. DAZ (Denormals Are Zero) only does anything for subnormal (aka denormal).