Search code examples
floating-pointx86-64simdssedivide-by-zero

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


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


Solution

  • 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).