0

I try to convert some numbers to string using snprintf. The name1 should have the same digits after comma as name2.

  #include <stdio.h>
  #define length 50

  int main()
  {
  char name1 [length]; 
  char name2 [length];
  double step= 0.00001;
  unsigned long long int iterMax  =100000000000; 
  int k;

  for (k = 0; k <= 20; k++)  
    { printf("numbers :  k = %2d ; k*step = %f ;", k, k*step); 
      snprintf(name1,length+1,"%f", iterMax+k*step); /* */
      snprintf(name2,length+1, " %f", k*step); /*  */
      printf("strings : k*step =  %s ; iterMax+k*step = %s \n",name2, name1);  
    }
  return 0;
}

Compile it with :

 gcc t.c  -Wall

Output is :

./a.out 
numbers :  k =  0 ; k*step = 0.000000 ;strings : k*step =   0.000000 ; iterMax+k*step = 100000000000.000000 
numbers :  k =  1 ; k*step = 0.000010 ;strings : k*step =   0.000010 ; iterMax+k*step = 100000000000.000015 
numbers :  k =  2 ; k*step = 0.000020 ;strings : k*step =   0.000020 ; iterMax+k*step = 100000000000.000015 
numbers :  k =  3 ; k*step = 0.000030 ;strings : k*step =   0.000030 ; iterMax+k*step = 100000000000.000031 
numbers :  k =  4 ; k*step = 0.000040 ;strings : k*step =   0.000040 ; iterMax+k*step = 100000000000.000046 

The results are the same ( digits aftter comma ) when iterMax is smaller , for example 100000000 :

numbers :  k =  0 ; k*step = 0.000000 ;strings : k*step =   0.000000 ; iterMax+k*step = 100000000.000000 
numbers :  k =  1 ; k*step = 0.000010 ;strings : k*step =   0.000010 ; iterMax+k*step = 100000000.000010 
numbers :  k =  2 ; k*step = 0.000020 ;strings : k*step =   0.000020 ; iterMax+k*step = 100000000.000020 
numbers :  k =  3 ; k*step = 0.000030 ;strings : k*step =   0.000030 ; iterMax+k*step = 100000000.000030 
numbers :  k =  4 ; k*step = 0.000040 ;strings : k*step =   0.000040 ; iterMax+k*step = 100000000.000040 

The ULLONG_MAX = 18446744073709551615 is greater then iterMax.

How can I resolve that ?

TIA

Adam
  • 1,254
  • 12
  • 25
  • What exactly do you want? – Igor Pejic Aug 15 '14 at 14:06
  • 3
    You're expecting very large floating point numbers to be very precise at the very-small-decimal end of things. That's just not how floating point works. See http://stackoverflow.com/questions/9765744/precision-in-c-floats, and many, many more questions about floating point precision. – Paul Roub Aug 15 '14 at 14:06
  • 2
    You are using `snprintf` and explicitly asking it to overrun your buffer? *Why???* – Deduplicator Aug 15 '14 at 14:12
  • 1
    If the first argument to `snprintf()` is `char buffer[SOMESIZE];`, the second argument should normally be `sizeof(buffer)`. Specifying a larger value than the array size invites the sort of buffer overflow that `snprintf()` is intended to prevent — and does prevent when use correctly. – Jonathan Leffler Aug 15 '14 at 14:20
  • I have added +1 after : http://stackoverflow.com/questions/9126637/snprintf-of-unsigned-long-appending-a-comma – Adam Aug 15 '14 at 14:49
  • The advice in that answer "You should pass strlen(ch) + 1 instead." is not so good. Follow the "Or even better, just sizeof(ch1)". – chux - Reinstate Monica Aug 15 '14 at 15:27

3 Answers3

5

This is actually a problem of double precision. There are plenty of other questions which explain more about IEEE-754 floating-point numbers, but I'll sum up the relevant points here:

  1. double and family effectively store numbers in scientific notation with limited precision. This means the larger the number, the less accurate it'll be.
  2. Most numbers use base 2. As such, the decimal 0.1 cannot be stored exactly (instead, it's something like 0.10000000149011612)

As such, the number 100000000000.000010 is "large", so it becomes less accurate after the decimal place. In fact, once you get towards about 4503599627370496, you can't even store all integers!

Drew McGowen
  • 11,471
  • 1
  • 31
  • 57
  • 2
    In ref to "They work in base 2, not base 10.": `double` uses base `FLT_RADIX` which is _commonly_ `2`. `IEEE-754` defines both base 2 and base 10 numbers. The binary formats being far more common. – chux - Reinstate Monica Aug 15 '14 at 14:20
  • snprintf(name1,length+1,"%f", log10(iterMax)+k*step); works good. Thx – Adam Aug 15 '14 at 14:29
1

Cast to a long double in order to get more precision:

snprintf(name1,length+1,"%Lf", (long double)iterMax+k*step); 

Output:

numbers :  k =  0 ; k*step = 0.000000 ;strings : k*step =   0.000000 ; iterMax+k*step = 100000000000.000000 
numbers :  k =  1 ; k*step = 0.000010 ;strings : k*step =   0.000010 ; iterMax+k*step = 100000000000.000010 
numbers :  k =  2 ; k*step = 0.000020 ;strings : k*step =   0.000020 ; iterMax+k*step = 100000000000.000020 
numbers :  k =  3 ; k*step = 0.000030 ;strings : k*step =   0.000030 ; iterMax+k*step = 100000000000.000030 
numbers :  k =  4 ; k*step = 0.000040 ;strings : k*step =   0.000040 ; iterMax+k*step = 100000000000.000040 
numbers :  k =  5 ; k*step = 0.000050 ;strings : k*step =   0.000050 ; iterMax+k*step = 100000000000.000050 
numbers :  k =  6 ; k*step = 0.000060 ;strings : k*step =   0.000060 ; iterMax+k*step = 100000000000.000060 
numbers :  k =  7 ; k*step = 0.000070 ;strings : k*step =   0.000070 ; iterMax+k*step = 100000000000.000070 
numbers :  k =  8 ; k*step = 0.000080 ;strings : k*step =   0.000080 ; iterMax+k*step = 100000000000.000080 
numbers :  k =  9 ; k*step = 0.000090 ;strings : k*step =   0.000090 ; iterMax+k*step = 100000000000.000090 
numbers :  k = 10 ; k*step = 0.000100 ;strings : k*step =   0.000100 ; iterMax+k*step = 100000000000.000100 
numbers :  k = 11 ; k*step = 0.000110 ;strings : k*step =   0.000110 ; iterMax+k*step = 100000000000.000110 
numbers :  k = 12 ; k*step = 0.000120 ;strings : k*step =   0.000120 ; iterMax+k*step = 100000000000.000120 
numbers :  k = 13 ; k*step = 0.000130 ;strings : k*step =   0.000130 ; iterMax+k*step = 100000000000.000130 
numbers :  k = 14 ; k*step = 0.000140 ;strings : k*step =   0.000140 ; iterMax+k*step = 100000000000.000140 
numbers :  k = 15 ; k*step = 0.000150 ;strings : k*step =   0.000150 ; iterMax+k*step = 100000000000.000150 
numbers :  k = 16 ; k*step = 0.000160 ;strings : k*step =   0.000160 ; iterMax+k*step = 100000000000.000160 
numbers :  k = 17 ; k*step = 0.000170 ;strings : k*step =   0.000170 ; iterMax+k*step = 100000000000.000170 
numbers :  k = 18 ; k*step = 0.000180 ;strings : k*step =   0.000180 ; iterMax+k*step = 100000000000.000180 
numbers :  k = 19 ; k*step = 0.000190 ;strings : k*step =   0.000190 ; iterMax+k*step = 100000000000.000190 
numbers :  k = 20 ; k*step = 0.000200 ;strings : k*step =   0.000200 ; iterMax+k*step = 100000000000.000200 
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
0

When printing more than DBL_DIG significant decimal digits, the effects of a double finite format may appear.

Example:

#include <float.h>

printf("%d\n", DBL_DIG);
printf("%.*e\n", DBL_DIG - 1, 100000000000.0 + 0.00001);
printf("%.*e\n", DBL_DIG - 1 + 10, 100000000000.0 + 0.00001);

15
1.00000000000000e+11
1.000000000000000152587891e+11

1.00000000000000e+11 has 15 significant decimal digits. (14 to the right of '.')


Regardless of the base (2, 10, 16, etc.) used for a double, only so many decimal digits are "round-trip"-able.

Example: (Assuming DBL_DIG is 10, the minimum specified by C)

double x;
scanf("%lf", &x);
printf("%.*e\n", DBL_DIG - 1, x);
printf("%.*e\n", DBL_DIG - 1 + 5, x);

Should the user entered "12345678901234567890", the output would be "1.234567890e19" and "1.234567890?????e19", the "?????" not begin specified by C.

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