1

Considering the following example. (CPU: Intel(R) Core(TM) i7-4790, gcc (GCC) 4.8.5 20150623)

// file test.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    srand(time(0));
    float i = rand();
    if (i < 0.042) {
        printf("i %f\n", i);
    }
}

compile the source code with

gcc test.c -O0 -g3

show the disassemble code with

objdump -S a.out

result present here

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
  4005fd:   55                      push   %rbp
  4005fe:   48 89 e5                mov    %rsp,%rbp
  400601:   48 83 ec 10             sub    $0x10,%rsp
    srand(time(0));
  400605:   bf 00 00 00 00          mov    $0x0,%edi
  40060a:   e8 e1 fe ff ff          callq  4004f0 <time@plt>
  40060f:   89 c7                   mov    %eax,%edi
  400611:   e8 ba fe ff ff          callq  4004d0 <srand@plt>
    float i = (float)rand() / (float)(RAND_MAX);
  400616:   e8 e5 fe ff ff          callq  400500 <rand@plt>
  40061b:   f3 0f 2a c0             cvtsi2ss %eax,%xmm0
  40061f:   f3 0f 10 0d d1 00 00    movss  0xd1(%rip),%xmm1        # 4006f8 <__dso_handle+0x10>
  400626:   00 
  400627:   f3 0f 5e c1             divss  %xmm1,%xmm0
  40062b:   f3 0f 11 45 fc          movss  %xmm0,-0x4(%rbp)
    if (i < 0.042) {
  400630:   f3 0f 10 45 fc          movss  -0x4(%rbp),%xmm0
  400635:   0f 5a c0                cvtps2pd %xmm0,%xmm0
  400638:   f2 0f 10 0d c0 00 00    movsd  0xc0(%rip),%xmm1        # 400700 <__dso_handle+0x18>
  40063f:   00 
  400640:   66 0f 2e c8             ucomisd %xmm0,%xmm1
  400644:   76 17                   jbe    40065d <main+0x60>
        printf("i %f\n", i);
  400646:   f3 0f 10 45 fc          movss  -0x4(%rbp),%xmm0
  40064b:   0f 5a c0                cvtps2pd %xmm0,%xmm0
  40064e:   bf f0 06 40 00          mov    $0x4006f0,%edi
  400653:   b8 01 00 00 00          mov    $0x1,%eax
  400658:   e8 53 fe ff ff          callq  4004b0 <printf@plt>
    }
}

I found that it read the 0.042 from a double-precision value and then convert the "i" as a double-precision value and then make a comparison. Why didn't it just load a single-precision(0.042) value and do the comparison? (which I think may result in better performance), and if I want to force it to do that, what should I do?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
colin-zhou
  • 185
  • 1
  • 15
  • 1
    by default `double` is used rather than `float`, and in expression they are converted to `double`, it is the same for `short` converted to `int` by default, etc – bruno Jun 07 '20 at 17:42
  • 1
    `0.042` is a double-precision constant, and C default promotions apply to promote `i` to `double`. Perhaps you mean `0.042f`, if you wanted a `float` constant? – Peter Cordes Jun 07 '20 at 17:46
  • If `0.042f` was exactly representable, GCC might have optimized to keep it a `float` (if you'd enabled optimizations and written it so it didn't fully optimize away), but it's not exactly representable so that wouldn't be legal. – Peter Cordes Jun 07 '20 at 17:53
  • @PeterCordes, Got it,thanks a lot. – colin-zhou Jun 08 '20 at 01:06

1 Answers1

2

Without suffix a floating point literal is a double, so 0.042 is a double. In the same way an integer literal is an int when its value can be a supported by a short or a char.

If you want a float use the suffix f or F

See floating point literal

bruno
  • 32,421
  • 7
  • 25
  • 37
  • 1
    Large integer literals *do* automatically promote to `long` or even `long long` if necessary. e.g. `0x123456789abcd` has type `long long`. (IIRC, unsigned versions of those types can be chosen implicitly, too.) This is unlike the situation for FP literals: they don't automatically promote to `long double` even in the rare case where it would be exactly representable as long double but not as double. (e.g. a huge integer value, or a long but still binary fraction.) – Peter Cordes Jun 08 '20 at 03:03
  • @PeterCordes yes you are right, I said wrongly, I corrected my answer, thank you for your remark – bruno Jun 08 '20 at 06:31
  • Ah, now I see the point you were making. Of course, `0.042` can't be exactly represented as a float, double, or long double (https://www.h-schmidt.net/FloatConverter/IEEE754.html), so the situation isn't exactly equivalent. (Unless you're on a hypothetical C implementation with decimal (not binary) floating point.) – Peter Cordes Jun 08 '20 at 06:36
  • @PeterCordes yes I am speaking about the compatibility with the min/max value of the types. And yes this is more complicated for the floating point and 0.042 cannot be exactly represented by any floating point whatever the size, nor even the 'simple' 0.1 – bruno Jun 08 '20 at 06:53
  • 1
    Ok that's fair; if you *were* designing automatic-promotion rules for FP literals, you could base it on required exponent and ignore mantissa rounding. That would be super sketchy (e.g. large magnitude constants would be rounded to nearest multiple of 2, 4, 8, and higher powers of 2 as magnitude increased, before switching over to long double if it existed and had a wider exponent range.) So there are obvious reasons why C wasn't designed that way. Anyway, a good compiler could optimize the compare to keep everything `float` if the constant was `0.5` or `0.25` or something `float` can repr. – Peter Cordes Jun 08 '20 at 07:02
  • 1
    But yes, your analogy works well enough if you don't try to examine it too deeply :P – Peter Cordes Jun 08 '20 at 07:03