2

I am trying to write a code in C that generates a random integer , performs simple calculation and then I am trying to print the values of the file in IEEE standard. But I am unable to do so , Please help.

I am unable to print it in Hexadecimal/Binary which is very important. If I type cast the values in fprintf, I am getting this Error expected expression before double.

int main (int argc, char *argv) {

     int limit = 20 ; double a[limit], b[limit];                //Inputs
     double result[limit]    ; int i , k ;            //Outputs
     printf("limit = %d", limit ); double q;


     for (i= 0 ; i< limit;i++)
         {
         a[i]= rand();
         b[i]= rand();
         printf ("A= %x B = %x\n",a[i],b[i]);

         }

     char op;

      printf("Enter the operand used : add,subtract,multiply,divide\n"); 
     scanf ("%c", &op); switch (op) {

     case '+': {
             for (k= 0 ; k< limit ; k++)
                 {

                 result [k]= a[k] + b[k];
            printf ("result= %f\n",result[k]);

                 }
             }
         break;

     case '*': {
             for (k= 0 ; k< limit ; k++)
                 {

                 result [k]= a[k] * b[k];

                 }
             }
         break;

     case '/': {
             for (k= 0 ; k< limit ; k++)
                 {

                 result [k]= a[k] / b[k];

                 }
             }
         break;

     case '-': {
             for (k= 0 ; k< limit ; k++)
                 {

                 result [k]= a[k] - b[k];

     }
             }
         break; }


     FILE *file; file = fopen("tb.txt","w"); for(k=0;k<limit;k++) {  
     fprintf (file,"%x\n
     %x\n%x\n\n",double(a[k]),double(b[k]),double(result[k]) );

     }


     fclose(file); /*done!*/
 }
Rais Alam
  • 6,970
  • 12
  • 53
  • 84
chitranna
  • 1,579
  • 6
  • 25
  • 42
  • I need the values in the text file to compare it with my Hardware design. Also , i am a beginner in C. I dont know anything – chitranna Mar 06 '13 at 04:31
  • A type-cast syntax is `(double)a[k]`, but you're converting a double to a double again. `%x` expects an `unsigned int` and `sizeof(double)` may be different from `sizeof(unsigned int)` most of the time. I suggest: [Double to hex string](http://stackoverflow.com/q/497472/260411) – Toribio Mar 06 '13 at 04:52

3 Answers3

3

If your C compiler supports IEEE-754 floating point format directly (because the CPU supports it) or fully emulates it, you may be able to print doubles simply as bytes. And that is the case for the x86/64 platform.

Here's an example:

#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <float.h>

void PrintDoubleAsCBytes(double d, FILE* f)
{
  unsigned char a[sizeof(d)];
  unsigned i;
  memcpy(a, &d, sizeof(d));
  for (i = 0; i < sizeof(a); i++)
    fprintf(f, "%0*X ", (CHAR_BIT + 3) / 4, a[i]);
}

int main(void)
{
  PrintDoubleAsCBytes(0.0, stdout); puts("");
  PrintDoubleAsCBytes(0.5, stdout); puts("");
  PrintDoubleAsCBytes(1.0, stdout); puts("");
  PrintDoubleAsCBytes(2.0, stdout); puts("");
  PrintDoubleAsCBytes(-2.0, stdout); puts("");
  PrintDoubleAsCBytes(DBL_MIN, stdout); puts("");
  PrintDoubleAsCBytes(DBL_MAX, stdout); puts("");
  PrintDoubleAsCBytes(INFINITY, stdout); puts("");
#ifdef NAN
  PrintDoubleAsCBytes(NAN, stdout); puts("");
#endif
  return 0;
}

Output (ideone):

00 00 00 00 00 00 00 00 
00 00 00 00 00 00 E0 3F 
00 00 00 00 00 00 F0 3F 
00 00 00 00 00 00 00 40 
00 00 00 00 00 00 00 C0 
00 00 00 00 00 00 10 00 
FF FF FF FF FF FF EF 7F 
00 00 00 00 00 00 F0 7F 
00 00 00 00 00 00 F8 7F 

If IEEE-754 isn't supported directly, the problem becomes more complex. However, it can still be solved.

Here are a few related questions and answers that can help:

And, of course, all the IEEE-754 related info can be found in Wikipedia.

Community
  • 1
  • 1
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • @EricPostpischil I'm expecting the OP to be able to figure it out since there's "hardware design" in the picture. – Alexey Frunze Mar 06 '13 at 11:30
  • @Alexey Frunze , If i want the order reversed, I mean for 1 (00 00 00 00 00 00 F0 3F) to write as (3F F0 00 00 00 00 00 00). How would I do that ? – chitranna Mar 07 '13 at 07:25
  • 1
    Change `a[i]` to `a[sizeof(d) - 1 - i]`. – Alexey Frunze Mar 07 '13 at 07:32
  • @Alexey Frunze, can you have a look at this [link](http://stackoverflow.com/questions/16182630/full-variation-of-random-numbers-in-c?noredirect=1#comment23133946_16182630) – chitranna Apr 24 '13 at 04:40
1

Try this in your fprint part:

fprintf (file,"%x\n%x\n%x\n\n",*((int*)(&a[k])),*((int*)(&b[k])),*((int*)(&result[k])));

That would translate the double as an integer so it's printed in IEEE standard.

But if you're running your program on a 32-bit machine on which int is 32-bit and double is 64-bit, I suppose you should use:

fprintf (file,"%x%x\n%x%x\n%x%x\n\n",*((int*)(&a[k])),*((int*)(&a[k])+1),*((int*)(&b[k])),*((int*)(&b[k])+1),*((int*)(&result[k])),*((int*)(&result[k])+1));

  • 1
    Converting pointers this way is not guaranteed by the C standard. The proper way to inspect the bytes of an object is by using unions (per my note on [this answer](http://stackoverflow.com/a/15239650/298225)) or pointers to character types (including using `memcpy`). – Eric Postpischil Mar 06 '13 at 11:27
  • That's true, since sizeof(int) depends on the platforms. Pointer to char would be a good improvement for my solutions, thx for mentioning that. –  Mar 06 '13 at 13:21
  • There are more considerations than just the object sizes. Different alignment requirements can cause traps. Optimization by the compiler may transform unsupported language uses into other behaviors. Unusual machines may have trap representations in the `int` type (`unsigned int` is safe). – Eric Postpischil Mar 06 '13 at 13:50
1

In C, there are two ways to get at the bytes in a float value: a pointer cast, or a union. I recommend a union.

I just tested this code with GCC and it worked:

#include <stdio.h>
typedef unsigned char BYTE;

int
main()
{
    float f = 3.14f;
    int i = sizeof(float) - 1;
    BYTE *p = (BYTE *)(&f);
    p[i] = p[i] | 0x80;  // set the sign bit
    printf("%f\n", f);  // prints -3.140000
}

We are taking the address of the variable f, then assigning it to a pointer to BYTE (unsigned char). We use a cast to force the pointer.

If you try to compile code with optimizations enabled and you do the pointer cast shown above, you might run into the compiler complaining about "type-punned pointer" issues. I'm not exactly sure when you can do this and when you can't. But you can always use the other way to get at the bits: put the float into a union with an array of bytes.

#include <stdio.h>

typedef unsigned char BYTE;

typedef union
{
    float f;
    BYTE b[sizeof(float)];
} UFLOAT;

int
main()
{
    UFLOAT u;
    int const i = sizeof(float) - 1;

    u.f = 3.14f;
    u.b[i] = u.b[i] | 0x80;  // set the sign bit
    printf("%f\n", u.f);  // prints -3.140000
}

What definitely will not work is to try to cast the float value directly to an unsigned integer or something like that. C doesn't know you just want to override the type, so C tries to convert the value, causing rounding.

float f = 3.14;
unsigned int i = (unsigned int)f;

if (i == 3)
    printf("yes\n"); // will print "yes"

P.S. Discussion of "type-punned" pointers here:

Dereferencing type-punned pointer will break strict-aliasing rules

Community
  • 1
  • 1
steveha
  • 74,789
  • 21
  • 92
  • 117
  • Using a union will almost certainly work, but strictly speaking, isn't it UB to access a union member other than the one most recently set? I thought that casting to `char *`, on the other hand, is explicitly defined to work. Or is the union case simply an extension of that? – Carl Norum Mar 06 '13 at 05:41
  • 1
    As far as I know, the C standard doesn't have an official recommendation as to how you should access the bits inside a float. I don't know of anything that is guaranteed and standard. In my experience, the union trick has worked for me on Windows, Linux, Mac, and various embedded processors including DSP processors. I recommend the union because it has always worked for me. I do wish that C had something like `reinterpret_cast` in C++ where you can explicitly tell the compiler "don't round anything, just change the type". – steveha Mar 06 '13 at 05:47
  • 1
    I hope my answer is clear enough, even though I used `float` instead of `double`. If anyone really cares I guess I could re-write it to use `double`. – steveha Mar 06 '13 at 05:49
  • 3
    @CarlNorum: No, it is not intrinsically undefined behavior. When a value is stored in a union member, the bytes in the union other than those for that object take unspecified values (C 2011 6.2.6.1 6 and 7). The bytes of the stored member of course take on a representation of the value. When a member other than the last-stored member is accessed, the corresponding bytes are reinterpreted as the new type (6.5.2.3 3 and note 95). The bytes can form a trap representation, so that can cause undefined behavior, but that does not happen when accessing char or unsigned integers. – Eric Postpischil Mar 06 '13 at 11:24
  • @steveha Thank you for the help and it was a good explanantion – chitranna Mar 07 '13 at 07:26
  • It doesn't matter what you do with your casts and unions: floating-point numbers in C aren't guaranteed to use IEEE754 representation, so none of that will portably get you IEEE754 bytes from a float. – Nicolás Apr 04 '13 at 03:49
  • @Nicolás, while it is true that platforms can use non-IEEE floating-point formats, all the major platforms do use standard floats. My example code is useful on Windows, Mac, Linux on x86 and ARM, and TI and ADI DSP chips. And that is just the list I have personally used. Yes, if you are on an unusual hardware platform you might not have IEEE standard floats, but you can at least use the `union` to get at the raw bits of the float value. The `union` trick is the closest thing to standard, portable C code for this. – steveha Apr 04 '13 at 06:02