basarts

Writing / reading unsigned long long using double channel

7 posts in this topic

I tried to write and read an unsigned long long value over a double channel, using a double value as starting point.

Code snippet:

 

    double d = 3.1415926535897931;

    unsigned long long * ullp = (unsigned long long *) &d;

    out_port.write(*ullp); // sc_out<double> out_port; connected via sc_signal<double> to in_port

    unsigned long long ull = in_port.read(); // sc_in<double> in_port; connected via sc_signal<double> to out_port

    double * dp = (double *) &ull;

 

When I print the values of d, *ullp, ull and *dp, I notice the following:

 

    d:     3.1415926535897931;

    *ullp: 4614256656552045848;

    ull:   4614256656552045568;

    *dp:   3.1415926535896688;

 

However, plain casting without SystemC (double d -> ull* ullp = (ull*) &d -> ull u = *ullp -> double* dp = (double*) &u -> double d2 = *dp) returns d2 with exactly the same value as d.

 

Any idea what is happening and why the channel of type double seems to loose a bit of its precision when using it in this way?

 

-- 

greetz,

Bas

Share this post


Link to post
Share on other sites

Hi Bas,

 

Did you mean to say:

 

    double d = 3.1415926535897931;

    unsigned long long * ullp = (unsigned long long *) &d;

    out_port.write(*ullp); // sc_out<doubleunsigned long long> out_port; connected via sc_signal<doubleunsigned long long> to in_port

    unsigned long long ull = in_port.read(); // sc_in<doubleunsigned long long> in_port; connected via sc_signal<doubleunsigned long long> to out_port

    double * dp = (double *) &ull;

 

Also, what is the output of sizeof(double) and sizeof(unsigned long long) in your case?

Share this post


Link to post
Share on other sites

Hi Karthick,

 

Thanks for your reply. I did mean sc_out<double> out_port and sc_in<double> in_port, because I was deliberately using a double channel.

 

The sizeof() operator returns 8 both for double and unsigned long long on my system.

 

In the meantime, I found that it is not related to SystemC anyway:

 

    double d = 3.1415926535897931;

    unsigned long long *ullp = (unsigned long long *) &d;

    unsigned long long ull = *ullp; // ull = 4614256656552045848

    double d2 = (double) ull; // d2 = 4614256656552045568.0000000000000000 !!

    unsigned long long ull2 = (unsigned long long) d2; // ull2 = 4614256656552045568

    double * dp = (double *) &ull2; // *dp = 3.1415926535896688

 

Hence, the casting of an unsigned long long to a double seems to cause the problem.

 

--

greetz,

Bas

Share this post


Link to post
Share on other sites

Hi Bas,

 

In trying to explain this a bit more, I looked up the double format. As per http://en.wikipedia.org/wiki/Double-precision_floating-point_format, the 8-byte double format can handle 15-17 significant decimal digits without loss of precision.

 

When assigning,

   double d2 = (double) ull;

the width of ull (4614 2566 5655 2045 848) is 19 significant decimal digits, which causes loss in precision.

Share this post


Link to post
Share on other sites

Hi, 

 

as you mentioned before, casting values is the problem here. If you want to keep the bit representation independent from the data types, you have to rely on casting pointers. 

unsigned long long ull = in_port.read();

This line converts the value and changes the bit representation. 

double d = in_port.read();
unsigned long long * ullp = (unsigned long long *)&d;
unsigned long long ull = *ullp;

Should work in the same way as before when you did write to the out port.

 

Greetings

Ralph

Share this post


Link to post
Share on other sites

I tried to write and read an unsigned long long value over a double channel, using a double value as starting point.

Code snippet:

 

    double d = 3.1415926535897931;

    unsigned long long * ullp = (unsigned long long *) &d;

    out_port.write(*ullp); // sc_out<double> out_port; connected via sc_signal<double> to in_port

    unsigned long long ull = in_port.read(); // sc_in<double> in_port; connected via sc_signal<double> to out_port

    double * dp = (double *) &ull;

 

When I print the values of d, *ullp, ull and *dp, I notice the following:

 

    d:     3.1415926535897931;

    *ullp: 4614256656552045848;

    ull:   4614256656552045568;

    *dp:   3.1415926535896688;

 

However, plain casting without SystemC (double d -> ull* ullp = (ull*) &d -> ull u = *ullp -> double* dp = (double*) &u -> double d2 = *dp) returns d2 with exactly the same value as d.

 

Any idea what is happening and why the channel of type double seems to loose a bit of its precision when using it in this way?

 

-- 

greetz,

Bas

Hello Sir,

Have you considered the possibility of using bit vectors of sizes specified by

you, instead of using 'long long' etc., After all, in a real world hardware, all

numbers are stored as bit vectors, so although it might appear somewhat

of a low-level approach, it would be far more intuitive, and there would not

be any of the casting issues that you face. Hope that helps.

David Black likes this

Share this post


Link to post
Share on other sites

Basic question: Why are you not just using a port that carries doubles? When modeling, you can assign timing independent of what the data format is. I make the presumption that you are doing high-level modeling rather than RTL.

 

If your answer is to store it in a memory model that uses unsigned long long int or as its base type, then you should consider:

// C++ program to test using unsigned long long int to carry data across a connection
#include <iostream>
#include <cassert>
using namespace std;
int main() {

  bool success = true; // innocent until proven otherwise

  typedef unsigned long long int  data_t; //< generic definition of the data passed over port
  assert(sizeof(double)<=sizeof(data_t)); //< ensure it is big enough

  for (size_t i=0; i!=1000; ++i) {
    // Create a random number
    double xmit_double = double(random())/double(1<<(random()%32));

    data_t& xmit_data = reinterpret_cast<data_t&>(xmit_double);

    // Send the data to the "other" end
    data_t  recv_data = xmit_data;

    double& recv_double = reinterpret_cast<double&>(recv_data);

    success &= (recv_double == xmit_double);
  }
  cout << (success?"PASSED":"FAILED") << endl;
  return success?0:1;
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now