20

Is there a difference between 0 and 0.0 in C++? Which should you use for initializing a double?

Thanks

Nosredna
  • 83,000
  • 15
  • 95
  • 122

4 Answers4

21

A literal 0 is considered to be an int literal; literal 0.0 is a double literal. When assigning to a double, either will work (since the int can be cast in a widening conversion); however, casting 0.0 to an int is a narrowing conversion, and must be done explicitly; i.e. (int)0.0.

Rob
  • 47,999
  • 5
  • 74
  • 91
  • 2
    and 0.0f is a float value. :D – Gordon Gustafson Sep 06 '09 at 00:32
  • 3
    @Rob, are you saying that a double cannot be implicitly converted to an int? Consider `int a = 0.0;`. – Johannes Schaub - litb Sep 06 '09 at 01:05
  • 1
    A double can most certainly be assigned to an int without an explicit cast. This part of the answer needs to be revised. – Brian Neal Sep 06 '09 at 14:59
  • 3
    Maybe Rob means "must" in the sense of "to avoid warnings on "? In the case of a constant which clearly is representable in an int, that seems overkill. But in general, conversion from double to int is dangerous because it's undefined for large values, so I certainly agree with the generalisation that narrowing conversions *should* be explicit. – Steve Jessop Sep 06 '09 at 16:12
  • @onebyone Of course it may not be desired but it is allowed implicitly without a cast by the language. – Brian Neal Sep 07 '09 at 03:38
  • 1
    I think 0 to 0.0 casting done in compile time. Am I right? – qwerty Jun 17 '15 at 11:01
13

I try to keep my constants type-consistent. 0 for ints. 0.0f or 0.f for float, and 0.0 for double.

To me, the most important reason to do this is so the compiler and the programmer see the same thing.

If I do this...

float t=0.5f;
float r;

r= 1 * t;

...should r be assigned 0 or .5? There's no hesitation if I do this instead...

float t=0.5f;
float r;

r= 1.0f * t;
Nosredna
  • 83,000
  • 15
  • 95
  • 122
6

One appears to be an integer literal, the other a floating point literal. It really doesn't matter to the compiler whether you initialize floats or doubles with integer literals. In any event the literal will be compiled into some internal representation.

I would tend to suggest 0.0 in order to make your intention (to other programmers) explicitly clear.

Jim Dennis
  • 17,054
  • 13
  • 68
  • 116
  • Actually, that may matter because the compiler may rather than loading 0, which is a single-cycle instruction, the compiler may load zero from ROM. With that said, that would be kind of silly, but then again we are monkies. –  Jul 31 '18 at 15:39
3

Here is the Visual-C++ disassembly for:

int main() {
  double x = 0;
  //
  double y = 0.0;
  //
  double z = x * y;
  return 0;
}

Disassembly

int main() {
00007FF758A32230  push        rbp  
00007FF758A32232  push        rdi  
00007FF758A32233  sub         rsp,128h  
00007FF758A3223A  mov         rbp,rsp  
00007FF758A3223D  mov         rdi,rsp  
00007FF758A32240  mov         ecx,4Ah  
00007FF758A32245  mov         eax,0CCCCCCCCh  
00007FF758A3224A  rep stos    dword ptr [rdi]  
  double x = 0;
00007FF758A3224C  xorps       xmm0,xmm0  
00007FF758A3224F  movsd       mmword ptr [x],xmm0  
  //
  double y = 0.0;
00007FF758A32254  xorps       xmm0,xmm0  
00007FF758A32257  movsd       mmword ptr [y],xmm0  
  //
  double z = x * y;
00007FF758A3225C  movsd       xmm0,mmword ptr [x]  
00007FF758A32261  mulsd       xmm0,mmword ptr [y]  
00007FF758A32266  movsd       mmword ptr [z],xmm0  
  return 0;
00007FF758A3226B  xor         eax,eax  
}
00007FF758A3226D  lea         rsp,[rbp+128h]  
00007FF758A32274  pop         rdi  
00007FF758A32275  pop         rbp  
00007FF758A32276  ret

They produced the same assembly. Visual-C++ uses the method of XOR-ing the XMM register with it'self. Had you first loaded the integer 0 then moved it into the XMM register it would have used an extra instruction. Given our hypothesis on how the 0.0 may be loaded as a literal, as well as the extra useless instruction load loading an integer 0 then moving it into a floating-point register, neither is optimal so it seems as though it really doesn't matter because the compiler writer we must assume this optimization has been know for a long time because it is kind of obvious. If 100% portability is required, then it is more portable to write an inline function that manually uses the XOR technique.