100

Case 1:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0.0)<<std::endl;
}

It compiles without any warnings and prints inf. OK, C++ can handle division by zero, (see it live).

But,

Case 2:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0)<<std::endl;
}

The compiler gives the following warning (see it live):

warning: division by zero [-Wdiv-by-zero]
     std::cout<<(d/0)<<std::endl;

Why does the compiler give a warning in the second case?

Is 0 != 0.0?

Edit:

#include <iostream>

int main()
{
    if(0 == 0.0)
        std::cout<<"Same"<<std::endl;
    else
        std::cout<<"Not same"<<std::endl;
}

output:

Same
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Jayesh
  • 4,755
  • 9
  • 32
  • 62
  • 9
    I assume it takes zero as an integer in the second case and drops a warning, even if the calculation would be done using double later on (which I think should be the behaviour when d is a double). – Qubit Jul 23 '18 at 08:11
  • 10
    It's a QoI issue, really. Neither the warning or the lack of a warning is something mandated by the C++ standard itself. Are you using GCC? – StoryTeller - Unslander Monica Jul 23 '18 at 08:13
  • 1
    Possible duplicate of [The behaviour of floating point division by zero](https://stackoverflow.com/questions/42926763/the-behaviour-of-floating-point-division-by-zero) –  Jul 23 '18 at 08:16
  • 3
    The compiler is not required to give a diagnostic in either case; when it does file under trying-to-be-helpful – Richard Critten Jul 23 '18 at 08:20
  • Have you tried this? http://coliru.stacked-crooked.com/a/ccaa25c3f2f424fb –  Jul 23 '18 at 08:24
  • 9
    [#650275 - gcc-4.6: Does only detect division by zero for int, not float - Debian Bug report logs](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=650275) – user202729 Jul 23 '18 at 09:36
  • 5
    @StoryTeller What is QoI? https://en.wikipedia.org/wiki/QoI ? – user202729 Jul 23 '18 at 09:38
  • 1
    @user202729 quality of implementation – bolov Jul 23 '18 at 09:41
  • 5
    With regards to your last question, "is 0 the same as 0.0?" The answer is the _values_ are the same, but as you found out, that doesn't mean they're identical. Different types! Just like 'A' is not identical to 65. – Mr Lister Jul 23 '18 at 10:27
  • [double d](https://vignette.wikia.nocookie.net/edwikia/images/d/d8/Various-double-d-ed-edd-n-eddy-15221978-1142-834.jpg/revision/latest?cb=20140609171602)? –  Jul 24 '18 at 00:46
  • What about `15/0.0`? – Solomon Ucko Jul 25 '18 at 02:05

7 Answers7

110

Floating point division by zero is well defined by IEEE and gives infinity (either positive or negative according to the value of the numerator (or NaN for ±0) ).

For integers, there is no way to represent infinity and the language defines the operation to have undefined behaviour so the compiler helpfully tries to steer you clear from that path.

However in this case, since the numerator is a double, the divisor (0) should be promoted to a double too and there's no reason to give a warning here whilst not giving a warning for 0.0 so I think this is a compiler bug.

Motti
  • 110,860
  • 49
  • 189
  • 262
  • 8
    Both are floating point divisions though. In `d/0`, `0` is converted to the type of `d`. –  Jul 23 '18 at 08:17
  • 44
    Note that C++ is not required to use IEEE 754 (even though I never seen a compiler using a different standard).. – Yksisarvinen Jul 23 '18 at 08:18
  • @hvd true, but casting 0 to double removes the warning. – Domso Jul 23 '18 at 08:19
  • 1
    @hvd, good point, it that case this looks like a compiler bug – Motti Jul 23 '18 at 08:25
  • 14
    I would agree that it should either warn in both cases, or not warn in both cases (depending on what the compiler's handling of floating division by zero is) – M.M Jul 23 '18 at 08:30
  • 1
    @Yksisarvinen I'm pretty sure that there are plenty if you code with microprocessors etc. Many of those chips require to use custom compilers from the manifacturer so they almost never follow the standard and may decide to not implement (full) IEEE754...Obviously if you work with desktops/servers then IEEE754 is practically guaranteed. – Giacomo Alzetta Jul 23 '18 at 12:21
  • 8
    Pretty sure that floating point division by zero is UB too - it's just that GCC implements it according to IEEE 754. They don't have to do that though. – Martin Bonner supports Monica Jul 23 '18 at 14:58
  • 1
    [Floating point divide by zero is a tough topic which](https://twitter.com/shafikyaghmour/status/913102870890790912) – Shafik Yaghmour Jul 23 '18 at 16:27
  • Although the effect of both divisions is the same, I disagree that this is a compiler bug. You can see my reasoning for this in this [answer](https://stackoverflow.com/a/51487264/3854787) – Not a real meerkat Jul 23 '18 at 21:12
  • @Yksisarvinen: `-ffast-math` is all it takes – user541686 Jul 24 '18 at 00:25
  • @Yksisarvinen The Sony PS/2 might be the system you've been looking for. The *format* for floats in that system are the same: 1.8.23 with an implied one bit in the mantissa, but the results most assuredly are not always IEEE-754 complaint. – dgnuff Jul 25 '18 at 05:59
43

In Standard C++, both cases are undefined behaviour. Anything may happen, including formatting your hard drive. You should not expect or rely on "return inf. Ok" or any other behaviour.

The compiler apparently decides to give a warning in one case and not the other, but this doesn't mean that one code is OK and one isn't. It is just a quirk of the compiler's generation of warnings.

From the C++17 standard [expr.mul]/4:

The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 21
    Not true, in floating point arithmetic's division by zero is well defined. – Motti Jul 23 '18 at 08:16
  • 9
    @Motti - If one limits themselves to the C++ standard alone, there are no such guarantees. The scope of this question is not well specified, to be frank. – StoryTeller - Unslander Monica Jul 23 '18 at 08:19
  • 9
    @StoryTeller I'm pretty sure (though I haven't looked at the standard document itself for this) that if `std::numeric_limits::is_iec559` is `true`, then division by zero for `T` is not UB (and on most platforms it's `true` for `double` and `float`, although to be portable you'll need to explicitly check that with an `if` or `if constexpr`). – Daniel H Jul 23 '18 at 08:22
  • 1
    @DanielH the standard doesn't say that – M.M Jul 23 '18 at 08:24
  • @StoryTeller It's reasonable to guess that the OP's code is compiled for a platform where that's `true` for `double`, and it's reasonable of the compiler to not warn for zero division in cases where it's actually defined. – Daniel H Jul 23 '18 at 08:24
  • 6
    @DanielH - "Reasonable" is actually quite subjective. If this question was tagged [tag:language-lawyer] there would be a whole other (much smaller) set of reasonable assumptions. – StoryTeller - Unslander Monica Jul 23 '18 at 08:26
  • @Quentin I saw that, but none of the answers there both *quoted the standard* and mentioned `is_iec559`, so I didn't know if that guarantee is actually in the standard or a bug in cppreference. See for example M.M's claim that the standard doesn't in fact enforce that. – Daniel H Jul 23 '18 at 08:27
  • 1
    @M.M I used tenses imprecisely. I *will be* a bit surprised, tomorrow when I check the standard, if what you say is true. It probably doesn't specify it right where you quoted, but that particular piece has to be somewhat constrained because I doubt it's saying that `x / 0` is always UB even if `x` is an instance of a class with a user-defined `operator/` (if it is I'll look up how to submit a DR, because that's clearly not the intent). – Daniel H Jul 23 '18 at 08:31
  • 1
    @DanielH the specification of operators in [expr] applies to the non-overloaded case; overloaded operators are covered under a different chapter. That is pointed out by the start of the expr chapter – M.M Jul 23 '18 at 08:36
  • @DanielH is right, though the logic in the standard is the other way around. If an implementation relies on the UB of division by zero to make it do anything other than what IEEE 754 specifies, then the standard says `std::numeric_limits::is_iec559` must be defined as `false`. It follows that if an implementation *does* define as `std::numeric_limits::is_iec559` as `true` (as common implementations do), that implementation can no longer rely on division by zero being undefined to make it behave it other ways. –  Jul 23 '18 at 10:25
  • @hvd the standard doesn't specify what you or DanielH said. (If you disagree then show a standard quote) – M.M Jul 23 '18 at 11:03
  • @M.M It's literally the definition of `is_iec559`: "True if and only if the type adheres to IEC 559 standard." If division by zero behaves differently from what that standard specifies, then the type does not adhere to that standard, does it? –  Jul 23 '18 at 11:04
  • 1
    @hvd That doesn't change the fact that [expr.mul]/4 says that division by 0 is undefined, with no exceptions listed – M.M Jul 23 '18 at 11:06
  • 2
    @M.M By your logic, are *any* implementations allowed to ever define `is_iec559` as `true`? If so, under what circumstances? –  Jul 23 '18 at 11:06
  • 1
    @hvd Presumably so, otherwise it wouldn't exist. But the C++ Standard explictly says that division by 0 is undefined, which means that the C++ Standard imposes no requirements on the behaviour. This seems to be a contradiction (for `is_iec559` being `true` implementations), I would resolve such cases by saying the direct statement in the C++ standard takes precedence over the behaviour included by reference to another standard. Someone else might argue that the standard is defective to contain a contradiction and [expr.mul]/4 should be updated to say "...except for is_iec559=true cases" – M.M Jul 23 '18 at 11:16
  • 5
    @M.M Remember that it's exactly what you said, but nothing more: undefined doesn't mean no requirements are imposed, it means no requirements are imposed *by the standard*. I would argue that in this case, the fact that an implementation defines `is_iec559` as `true` means that the *implementation* is documenting behaviour that the standard leaves undefined. It's just that this is one case where the implementation's documentation can be read programmatically. Not even the only one: the same applies to `is_modulo` for signed integer types. –  Jul 23 '18 at 11:21
  • 2
    @hvd But your whole argument is that if the implementation defines `is_iec559` as `true` then the C++ standard imposes the requirement that the type follow IEC559. The `is_modulo` topic is a whole nother debate; the current standard explicitly says "`is_modulo` is false for signed integer types (6.9.1) unless an implementation, as an extension to this International Standard, defines signed integer overflow to wrap" . There isn't any analogous statement for `is_iec559` (but perhaps there should be, if that is the intent) – M.M Jul 23 '18 at 11:35
  • 1
    @M.M It says that in an example. Examples are non-normative and merely clarify the normative text. In the normative text, I do not see a substantial difference between `is_iec559` and `is_modulo`. I agree that it would be helpful to add a similar example to `is_iec559`. –  Jul 23 '18 at 11:42
  • @hvd the comments on [this other answer](https://stackoverflow.com/a/42926875/1505939) resemble what's happened on this thread – M.M Jul 23 '18 at 11:48
  • 1
    @Motti whether the implementation support IEEE754 is a [tricky topic and there is room for making either argument but technically still UB](https://twitter.com/shafikyaghmour/status/913102870890790912) – Shafik Yaghmour Jul 23 '18 at 16:28
  • I don't know about C++, but in C, [Annex F](https://port70.net/~nsz/c/c11/n1570.html#F) includes [actual examples](https://port70.net/~nsz/c/c11/n1570.html#F.8.4) of division by zero being defined behavior when Annex F is in effect, despite [6.5.5](https://port70.net/~nsz/c/c11/n1570.html#6.5.5) saying that "if the value of the second operand is zero, the behavior is undefined". – user2357112 Jul 23 '18 at 20:18
  • @user2357112 C++ has one more complication: undefined behaviour renders an expression non-constant, meaning an implementation must issue a diagnostic for `constexpr double d = 1.0 / 0.0;` regardless of whether IEEE 754 is supported and the behaviour is actually sort-of defined at the same time. –  Jul 26 '18 at 07:49
14

My best guess to answer this particular question would be that compiler emits warning before performing conversion of int to double.

So, the steps would be like this:

  1. Parse expression
  2. Arithmetic operator /(T, T2), where T=double, T2=int.
  3. Check that std::is_integral<T2>::value is true and b == 0 - this triggers warning.
  4. Emit warning
  5. Perform implicit conversion of T2 to double
  6. Perform well defined division (since compiler decided to use IEEE 754).

This is of course speculation and is based on compiler-defined specifications. From standard point of view, we're dealing with possible Undefined Behaviours.


Please note that this is expected behaviour according to GCC documentation
(btw. it seems that this flag can't be used explicitly in GCC 8.1)

-Wdiv-by-zero
Warn about compile-time integer division by zero. This is default. To inhibit the warning messages, use -Wno-div-by-zero. Floating point division by zero is not warned about, as it can be a legitimate way of obtaining infinities and NaNs.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
  • 2
    This is not how C++ compilers work. The compiler has to perform overload resolution on `/` to know that it's division. If the left hand side would have been a `Foo` object, and there would be a `operator/(Foo, int)`, then it might not even be division. The compiler only knows it's division when it has chosen `built-in / (double, double)` using an implicit conversion of the right-hand side. But that means it is NOT doing a division by `int(0)`, it's doing a division by `double(0)`. – MSalters Jul 23 '18 at 08:45
  • @MSalters [Please see this.](https://en.cppreference.com/w/cpp/language/operator_arithmetic) My knowledge about C++ is limited, but according to the reference `operator /(double, int)` is certainly acceptable. Then, it says conversion is performed before any other action, but GCC could squeeze in a quick check if `T2` is integer type and `b == 0` and emit a warning if so. Not sure if that's fully standard compliant, but compilers have full freedom in defining warning and when they should be triggered. – Yksisarvinen Jul 23 '18 at 09:31
  • 2
    We're talking about the built-in operator here. That's a funny one. It's not actually a function, so you can't take its address. Therefore you can't determine if `operator/(double,int)` really exists. The compiler may for instance decide to optimize `a/b` for constant `b` by replacing it with `a * (1/b)`. Of course, that means you are no longer calling `operator/(double,double)` at runtime but the faster `operator*(double,double)`. But it's now the optimizer which trips over `1/0`, the constant it would have to feed to `operator*` – MSalters Jul 23 '18 at 11:10
  • @MSalters Generally floating point division cannot be replaced with multiplication, probably apart from exceptional cases, such as 2. – user202729 Jul 23 '18 at 11:52
  • 2
    @user202729: GCC does it even for **integer** division. Let that sink in for a moment. GCC replaces integer division with integer multiplication. Yes, that's possible, because GCC knows it's operating on a ring (numbers modulo 2^N) – MSalters Jul 23 '18 at 12:12
  • @user202729: My point is that GCC can even handle the very complex case of integer division strength reduction. FP division strength reduction is straightforward in comparison. `f/1.25 => f*0.8` etc. – MSalters Jul 23 '18 at 12:27
  • It's easy to know who is correct. Just go to godbolt and compile `double f(double x){return x/3;}`. – user202729 Jul 23 '18 at 12:30
  • @MSalters A compiler does not need to consider overload resolution for an operator unless one operand has class or enum type or is type-dependent. – aschepler Jul 24 '18 at 02:25
9

I will not go into the UB / not UB debacle in this answer.

I just want to point that 0 and 0.0 are different despite 0 == 0.0 evaluating to true. 0 is an int literal and 0.0 is a double literal.

However in this case the end result is the same: d/0 is floating point division because d is double and so 0 is implicitly converted to double.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • 5
    I don't see how this is relevant, given that the usual arithmetic conversions specify that dividing a `double` by an `int` means that the `int` is converted to `double` , and it is specified in the standard that `0` converts to `0.0` (conv.fpint/2) – M.M Jul 23 '18 at 08:27
  • @M.M the OP wants to know if `0` is the same as `0.0` – bolov Jul 23 '18 at 08:32
  • 2
    The question says "Is `0 != 0.0` ?". OP never asks if they are "the same". Also it seems to me that the intent of the question is to do with whether `d/0` can behave differently to `d/0.0` – M.M Jul 23 '18 at 08:35
  • 2
    @M.M - [The OP did ask](https://stackoverflow.com/revisions/51474239/5). They aren't really showing decent SO netiquette with those constants edits. – StoryTeller - Unslander Monica Jul 23 '18 at 11:16
7

I would argue that foo/0 and foo/0.0 are not the same. Namely, the resulting effect of the first (integer division or floating point division) is highly dependant on the type of foo, while the same is not true for the second (it will always be a floating point division).

Whether any of the two is UB is irrelevant. Quoting the standard:

Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

(Emphasis mine)

Consider the "suggest parentheses around assignment used as truth value" warning: The way to tell the compiler that you really want to use the result of an assignment is by being explicit, and adding parenthesis around the assignment. The resulting statement has the same effect, but it tells the compiler you know what you're doing. The same can be said about foo/0.0: Since you're explicitly telling the compiler "This is floating point division" by using 0.0 instead of 0, the compiler trusts you and will not issue a warning.

Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55
  • 1
    Both have to undergo the *Usual arithmetic conversions* to get them to a common type, which will leave both cases floating point division. – Shafik Yaghmour Jul 24 '18 at 04:22
  • @ShafikYaghmour You missed the point in the answer. Note that I never mentioned what is the type of `foo`. That is intentional. Your assertion is only true in the case that `foo` is a floating point type. – Not a real meerkat Jul 24 '18 at 16:02
  • I did not, the compiler has type information and understand conversions, perhaps a text based static analyzer may be caught out by such things but the compiler should not. – Shafik Yaghmour Jul 24 '18 at 16:04
  • My point is that yes, the compiler knows the usual arithmetic conversions, but it chooses not to issue a warning when the programmer is being explicit. The whole point is that this is probably not a bug, but instead it is intentional behavior. – Not a real meerkat Jul 24 '18 at 16:10
  • Then the documentation I [pointed to](https://stackoverflow.com/a/51488448/1708801) is incorrect, since both cases are floating-point division. So either the documentation is wrong or the diagnostic has a bug. – Shafik Yaghmour Jul 24 '18 at 16:50
4

This looks like a gcc bug, the documentation for -Wno-div-by-zero clearly says:

Do not warn about compile-time integer division by zero. Floating-point division by zero is not warned about, as it can be a legitimate way of obtaining infinities and NaNs.

and after Usual arithmetic conversions covered in [expr.arith.conv] both operands will be double:

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:

...

Otherwise, if either operand is double, the other shall be converted to double.

and [expr.mul]:

The operands of * and / shall have arithmetic or unscoped enumeration type; the operands of % shall have integral or unscoped enumeration type. The usual arithmetic conversions are performed on the operands and determine the type of the result.

With respect to whether floating point divide by zero is undefined behavior and how different implementation deal with it seem my answer here. TL;DR; It looks like gcc conforms to Annex F wrt to floating point divide by zero, so undefined does not play a role here. The answer would be different for clang.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
2

Floating point division by zero behaves differently than integer division by zero.

The IEEE floating point standard differentiates between +inf and -inf, while integers cannot store infinity. Integer division by zero result is undefined behaviour. Floating point division by zero is defined by the floating point standard and results in +inf or -inf.

Rizwan
  • 3,324
  • 3
  • 17
  • 38
  • 2
    This is true but it’s not clear how this is relevant to the question, since *floating point division is performed in both cases*. There’s no integer division in OP’s code. – Konrad Rudolph Jul 23 '18 at 16:48