3

I want to write an IEEE-754 compliant division in C++, especially with regard to division by zero handling: positive/0->Inf, negative/0->-Inf, everything else/0->NaN.

While a simple C++ division on my machine results in this very behavior, the C++ standard does not mandate this semantics. It is instead undefined, so I cannot rely on it.

So what is the fastest way to implement this in C++? Of course I can do an explicit test like this:

double fdiv64(double numerator, double denominator)
{
   using limits=std::numeric_limits<double>;
   if (denominator==0.0) {
      if (numerator>0.0) {
         return limits::infinity();
      } else if (numerator<0.0) {
         return -limits::infinity();
      } else {
         return limits::quiet_NaN();
      }
   } else {
      return numerator/denominator;
   }
}

But this introduces extra branches and is totally superfluous on my machine, as I get the right behavior on it anyway. There does not seem to be a compiler intrinsic that does IEEE-754 compliant division. I could use inline assembly but this is quite unportable, too.

So what is the fastest way to do this division?

gexicide
  • 38,535
  • 21
  • 92
  • 152
  • The language standards cannot possibly include and track IEEE-754 standard. https://stackoverflow.com/a/30245885/412080 – Maxim Egorushkin Jun 23 '17 at 09:34
  • 1
    Run it on hardware that already does so. Seriously. The mainstream plaforms already do AFAIK. – user207421 Jun 23 '17 at 09:47
  • Why are you asking? Arithmetic operations are performed by the CPU when possible. Some 8-bit microcontrollers like those found in some Arduino boards don't even have FPUs. C++ doesn't dictate what the *CPU* will do. All desktop and server CPUs follow IEEE-754 since the 486 days. So do smartphone processors – Panagiotis Kanavos Jun 23 '17 at 09:47
  • @EJP define mainstream. It's not what it used to be, with IoT and microcontrollers. Desktops, servers, smartphones have FPUs. – Panagiotis Kanavos Jun 23 '17 at 09:48
  • hack it into your keyboard as fast as your fingers allow! I can write that in well under 10sec. – Walter Jun 23 '17 at 09:49
  • @PanagiotisKanavos: Why am I asking: The clang undefined behavior sanitizer told me that I have a division by zero in my code and thus invoke undefined behavior (and I do have, it is intended, but I anticipate IEEE 754 behavior!). – gexicide Jun 23 '17 at 10:47
  • 1
    Note that in portable C++, you can't even assume the _format_ of floaing-point values is IEEE-754. `float` could be 42 bits. So you're inherently in non-portable territory; the only question is how far. – MSalters Jun 23 '17 at 10:54
  • 1
    @gexicide anticipated but exceptional. You aren't supposed to divide by zero. IEEE-754 defines these values as *exceptional* and uses them to preserve the sign in case of underflow. You are already using these values in a non-compliant way. – Panagiotis Kanavos Jun 23 '17 at 11:08
  • 1
    You can check, with `std::numeric_limits::is_iec559`. (IEC 559 is another name for IEEE 754) – Pete Becker Jun 23 '17 at 14:53

2 Answers2

4

You can't do this portably I'm afraid, unless you build your own implementation of IEEE754 floating point in software for platforms that don't use it natively, and even then there'd be subtle differences since your type would not be a built-in type (e.g. && and || would not be short-circuited if you overloaded them); although user defined literals could be of help.

For example, support for infinity is optional, and is only present on a platform if std::numeric_limits<T>::has_infinity == true for your type T.

So trying to optimise your purportedly portable code is not even on the table.

References:

http://en.cppreference.com/w/cpp/types/numeric_limits/infinity http://en.cppreference.com/w/cpp/language/user_literal

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
2

You might already have that, without doing anything special. On several architectures, you can specify what should the CPU do in "special" situations (divide by zero, etc.). Have a look at fesetenv. Compilers usually have switches about how they should handle floating point math. For example, have a look at Visual Studio's cl.exe option about it.

If you stay on x86 architecture (and use SSE or x87 with internal precision set to float/double, see the links below for details), I think you can rely on IEEE754 behavior out of the box (so your program will work on any x86 machine, not just yours).

If you have a CPU which cannot follow IEEE754, then you might have to use a full software floating point implementation. Or maybe you can use the HW, and you only have to handle the exceptional cases.

But usually, today, almost everything (PC, mobile devices) uses IEEE754:

In a SW implementation, you have to extract sign, exponent, mantissa from the floating point number, and do the math yourself.

Note: You can have inf result even when the divisor is not zero, but a small (subnormal) number. It is because the max. number for a float is in the ~10^38 range, but the minimal, but positive number is in the ~10^-45 range.

geza
  • 28,403
  • 6
  • 61
  • 135
  • The second link points out that almost everything uses IEEE754 _layout_, but most implementations break a few rules. And that's especially so for edge cases, like the one in this question. – MSalters Jun 23 '17 at 10:57
  • @MSalters: I've added to bullet points into my answer. Do you know any commonly used CPU which is not IEEE754 compatible? Game consoles sometimes not fully compatible (PS2 VU had an 1 ulp error on multiply, PS3's SPU is not fully compatible either, but these affect very few programmers) – geza Jun 23 '17 at 20:53
  • AFAIK the ARM VFP isn't IEEE754 compatible in hardware ("RunFast" mode) but can use interrupts and software support to fully implement IEEE754. – MSalters Jun 24 '17 at 10:34