0

Let us consider the following two pieces of code:

#include <stdio.h>
int main(){
    float pi = 3.14;
    float r,circumference ;
    scanf("%f",&r);
    circumference  = 2*pi*r;
    printf("Circumference:%f\n",circumference );
    return 0;
}

In this one, I'm storing the value 3.14 in the variable pi.

#include <stdio.h>
int main(){
    float r,circumference ;
    scanf("%f",&r);
    circumference  = 2*3.14*r;
    printf("Circumference:%f\n",circumference );
    return 0;
}

In this one, I'm using directly the literal value 3.14.

Which one is the best option and why? What is the difference between both options regarding the memory consumption and processing time?

Zaratruta
  • 2,097
  • 2
  • 20
  • 26
  • 3
    For a better comparison, change `3.14` to `3.14f` to avoid conversions from `float` to `double` and back to `float` again in the version that multiplies by the literal. – Ian Abbott May 30 '23 at 17:15
  • There are further options: `float const pi = ...` and `#define PI ...` – well, under the hoods the latter is actually identical to your second example but having constants instead of magic numbers always is preferrable... – Aconcagua May 30 '23 at 17:16
  • 4
    I recommend you use e,g. [the compiler explorer](https://godbolt.org) to compare the assembly code generated for both programs. Remember to build with optimizations enabled. – Some programmer dude May 30 '23 at 17:16
  • 2
    I personally would go with `#define` in just one single location (-> header!) and give pi a much better precision – and have a `double` constant (just as well as `double` variables), all calculations are done on the same FPU (with same precision) anyway and there's just no reason to give up precision... – Aconcagua May 30 '23 at 17:19
  • 3
    For a trivial program like this there's effectively zero difference. Inside some "hot" code running billions of times you'll want to inspect the assembly output if you're concerned about performance, and benchmark to identify a *measurable* performance problem. This code will run very quickly and 99.9999% of the time will be spent waiting for the human to enter data. Heck, you could probably *compute* Pi from scratch and the output latency would barely differ. – tadman May 30 '23 at 17:20
  • Detail: "I'm using directly the literal value 3.14." --> In C, `3.14` is a _floating point constant_ of type `double`. C has two literals: _string literal_ and _compound literal_, both of which the address can be taken. – chux - Reinstate Monica May 30 '23 at 17:21
  • 1
    @tadman Appears to me that you still have far too few digits/nines after the comma ;) – Aconcagua May 30 '23 at 17:23
  • 1
    On a different note, there's almost no reason to use the `float` type on a normal PC system. The "native" floating point type is `double`. – Some programmer dude May 30 '23 at 17:28
  • @Aconcagua Precisely. You could make this program a nano-percent faster with more optimizations! – tadman May 30 '23 at 17:30
  • Yes. I would use macros for defining PI in this contexts. But this is just an example. My intention was to discuss the general case. Let us think about a program with hundreds of thousands of values like that, for example. – Zaratruta May 31 '23 at 01:32
  • Regarding the use o the term "literal", some educational texts about C programming call those raw values as literals. This is not correct? – Zaratruta May 31 '23 at 01:33

2 Answers2

2

Which one is the best option and why? What is the difference between both options regarding the memory consumption and processing time?

The memory consumption and processing time has little difference.

The 2*3.14*r; is a double calculation and 2*pi*r is a float one and so the 2nd may take a tad less or more memory and time. Not because of constant vs. variable difference, but because of type differences and conversions.

Even if example code was corrected and used the same floating point types throughout, the difference is scant. Save your coding time for larger optimizations and let a well enabled compiler do low level optimizations. Is premature optimization really the root of all evil?

  • Code for clarity.

  • Use types uniformly.

  • Use double floating point. Only use float, long double when there exist compelling reason for it.

    #define MY_PI     3.1415926535897932384626433832795
    #define MY_PI_F   3.1415926535897932384626433832795f
    #define MY_PI_LD  3.1415926535897932384626433832795L
  
    float r = ...
    float circumference = 2*MY_PI_F*r;
    // or with double
    double r = ...
    double circumference = 2*MY_PI*r;

Note that OP's 2 codes are not functionally the same 39.4% of the time due to type changes with the double calculation providing better answers.

#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>

int main() {
  unsigned long fi = 0, di = 0;
  float pi = 3.14;
   for (float r = FLT_MAX; r > 0.0; r = nextafterf(r, 0.0)) {
     fi++;
     float circumference1  = 2*3.14*r;
     float circumference2  = 2*pi*r;
     if (circumference1  != circumference2) {
       if (di == 0) printf("%.9g %.9g %.9g\n", r, circumference1, circumference2);
       di++;
     }
   }
   printf("Differences: %.1f%%\n", 100.0*di/fi);
}

When using float circumference1 = 2*3.14f*r;, I found no functional difference, yet that is not a certainty in C.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

The short answer is that literals are usually better for memory than variables. Or no difference thanks to compiler optimization.

Immediate values, a.k. numeric literals are stored directly inside instructions*. It is stored in the code segment. If you use variables, you allocate memory on the stack during runtime. Unless the compiler optimized the value out.

String literals are stored in .text segment as arrays of chars. They consume memory anyway. String variables are pointers. The value of the address of the start of the array is stored in a variable and allocated on the stack.

If you pass the string literal as an argument, the immediate value of the address will be pushed to the stack and consume little memory.

If you like to get the advantage of both readability and memory, you may like to use macros with #define. It will allow to give names to magic numbers.

#define PI 3.14

Macros are the words that will be replaced by their actual values before compilation by a preprocessor. There are many interesting things you can achieve with them.

*depending on the architecture. For example, in ARM some values cannot be represented by instruction and have to be stored in read-only memory.

Andrey Kachow
  • 936
  • 7
  • 22
  • 1
    Floating-point constants are not often stored as immediate operands in instructions. Even a `float` is nominally 32 bits, so it will not fit inside a 32-bit instruction. Some architectures have provision for encoding a small subset of floating-point constants as immediates, and some architectures may provide ways of loading full 32-bit values from the instruction stream, but they are often stored in a constant-data section, much as if they had been declared `static const float x = 3.14f;`. – Eric Postpischil May 30 '23 at 22:05
  • 2
    Generally, this answer is written broadly, likely too broadly to convey specific knowledge to students. For example, “Macros are the words that will be replaced by their actual values” is not really correct. Given `#define foo 3 * 4`, `foo` will not be replaced by its “value,” which a student might think is 12, but by the preprocessor tokens `3`, `*`, and `4`, which will be subsequently parsed in the C grammar. And “String variables are pointers” is similarly broad or vague. There is nothing the C standard calls a “string variable.” There are both pointers to `char` and arrays of `char`. – Eric Postpischil May 30 '23 at 22:08