Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / September 2006

Tip: Looking for answers? Try searching our database.

Socket & PrintWriter issue-- writing a float to a C client

Thread view: 
Jim Bancroft - 08 Sep 2006 07:09 GMT
Hi everyone,

I have a Java class that opens a socket and connects with a client.  The
client is written in C.

All goes well, but when it comes time to write a floating point value the
client has trouble reading it.  I realize this isn't a C newsgroup but I was
hoping someone's seen what I'm experiencing and can help?

In a nutshell the java server looks like this:

SocketServer listener;
int portNumber;
Socket mySocket;
PrintWriter outp;

listener= new ServerSocket(portNumber);
mySocket=listener.accept();
outp = new PrintWriter(mySocket.getOutputStream(),true);
outp.println(85.6f);

The C client does this:

char[128] buffer;
int num;
float flt;

memset(buffer,'\0',sizeof(buffer));
n = read(sockfd,buffer,127);
flt=atof(buffer);
printf("You returned %lf from the server\n",flt);

The printf statement shows a value of 0.0000 no matter what floating point
value I send via the java server.  If I send an integer from the server, I
have no problems using atoi() and printing it out.

Again, I know this is a java newsgroup but does anyone happen to know what
I'm doing wrong in my C code above?
Gordon Beaton - 08 Sep 2006 07:52 GMT
> All goes well, but when it comes time to write a floating point
> value the client has trouble reading it. I realize this isn't a C
> newsgroup but I was hoping someone's seen what I'm experiencing and
> can help?

You should be using strtod() or strtof() instead of atof() if you want
to catch conversion errors. I suggest you display the text received by
the C program and compare it with the format accepted by strtod().

Or if you use sscanf() you can be more specific about what gets
matched.

There are also Locale issues to consider. Does the float contain
commas, or decimals?

/gordon

Signature

[ don't email me support questions or followups ]
g o r d o n  +  n e w s  @  b a l d e r 1 3 . s e

Jim Bancroft - 09 Sep 2006 21:27 GMT
Hi,

> There are also Locale issues to consider. Does the float contain
> commas, or decimals?

This partcular float contains decimals but no floats.

I've changed things a bit since I posted.  Now I'm just trying to read an
integer, not a float.  Still having troubles.  Here's what I have on the
java side of the fence:

int myBalance;
DataOutputStream output;
//....
output.writeBytes(Integer.toString(myBalance));

In my C client I do this:

char mybuf[4];
int myInt;

bzero(mybuf, sizeof(mybuf));
read(sockfd,mybuf,sizeof(mybuf));
myInt = atoi(mybuf);

printf("my value is: %d\n", myInt);

...For some reason, when myBalance equals 85, I oscillate:  sometimes I
print out "8585" and other times zero.  I have no idea what's going on
there?  Is java writing out the string with two bytes per character, while C
only interprets things as one byte per char?  If so, is there a way to tell
java to use simple character encoding?
Arne Vajhøj - 10 Sep 2006 01:08 GMT
> I've changed things a bit since I posted.  Now I'm just trying to read an
> integer, not a float.  Still having troubles.  Here's what I have on the
[quoted text clipped - 18 lines]
> ...For some reason, when myBalance equals 85, I oscillate:  sometimes I
> print out "8585" and other times zero.

I am pretty sure that your problem is on the C side.

Try test on how many bytes read actually read and what
is the content of the bytes.

Arne
EJP - 10 Sep 2006 05:57 GMT
> In my C client I do this:
>
[quoted text clipped - 4 lines]
> read(sockfd,mybuf,sizeof(mybuf));
> myInt = atoi(mybuf);

atoi() needs a trailing zero and it hasn't got one here.
Arne Vajhøj - 10 Sep 2006 18:55 GMT
>> In my C client I do this:
>>
[quoted text clipped - 6 lines]
>
> atoi() needs a trailing zero and it hasn't got one here.

Depends on how many bytes read actually reads.

If it reads 4 bytes then it does not have a trailing
nul byte.

Arne
Jean-Francois Briere - 08 Sep 2006 08:56 GMT
Rather do:

// Java:

SocketServer listener;
int portNumber;
Socket mySocket;
DataOutputStream outp;

listener= new ServerSocket(portNumber);
mySocket=listener.accept();
outp = new DataOutputStream(mySocket.getOutputStream());
outp.writeFloat(85.6f);

// C:

char buffer[4];
int num;
float flt;

num = read(sockfd, buffer, 4);
memcpy(&flt, buffer, 4);
printf("You returned %f from the server\n", flt);

Regards
Dale King - 08 Sep 2006 14:15 GMT
> Rather do:
>
[quoted text clipped - 19 lines]
> memcpy(&flt, buffer, 4);
> printf("You returned %f from the server\n", flt);

I hope that was meant as a joke as it is the *WORST* way to do this. It is
non-portable. You are assuming that Java and C use the same binary
representation for their floating point values, which is a completely
unfounded assumption.

Using text is the most portable to transfer floating point values between
heterogenous environments. You can use the standard print conversion, but
that is actually not the most accurate way. It assumes that the receiving
end uses the same size of float. If I were writing float on the Java side
and double on the C side this would actually be introducing inaccuracy. The
standard conversion on the Java side is not the exact value of the float or
double, but the minimum number of digits that will be converted back to that
value using the same precision.

The most portable way to send the exact value is to do:

 system.out.println( new BigDecimal( 85.6f ).toString() );
Signature

Dale King

Chris Uppal - 08 Sep 2006 15:29 GMT
> > char buffer[4];
> > int num;
[quoted text clipped - 8 lines]
> representation for their floating point values, which is a completely
> unfounded assumption.

However the assumption /could/ be true in the OP's environment.  Or more likely
some related assumption, such as that a float is represented in C memory as 4
bytes in IEEE little-endian format -- in which case something like the above
code but with an additional byte-swapping step would be correct.

The key is to do one of three things.

1)    Create an interchange format which is designed to be portable and
      which is text based -- as Dale suggests.

2)    Create an interchange format which is deigned to be portable and
       which is binary based -- in which case the spec for the format must
       lay down the exact layout at the bits and bytes level.  For instance
           "the next four bytes are a 32-bit IEEE floating point
           number in little-endian format"
       Obviously your C and Java code will reflect the specification.

3)    Create an interchange format which is /not/ designed to be portable
      and which is binary based.  In that case you have no real control
       over when it stops working unless you control completely the
           - machines it runs on
           - compiler (make and version) the compiles it
           - compiler options

(3) is obviously irresponsible, but there's no general reason to prefer (1)
over (2) or vice versa.   Of course, if you /do/ choose to use (2) then there's
nothing to stop you choosing the format to be one that you can easily implement
for your current /actual/ machines.  In which case you might easily end up with
an implementation which looked like the above code on the 'C' end of the link.

To the OP:  If you are writing the data out using a java.io.DataOutputStream,
then you are implicitly defining a spec like (2).  So you should read the
javadoc for DataOutputStream to understand what it puts on the net at the
byte-level (actually it will write a float as 4 bytes in big-endian order).
Then you should write your C code to parse that.  If you are running on
ordinary Intel-like machinery then the layout of a C float in memory is
little-endian, so you could use code like the above, but you'd have to reverse
the bytes before converting them to a float.

   -- chris
Dale King - 08 Sep 2006 16:11 GMT
>> > char buffer[4];
>> > int num;
[quoted text clipped - 11 lines]
>
> However the assumption /could/ be true in the OP's environment.

Which is basically what I said when I said it was non-portable. It will work
on some environments, but will not work in many others. It could break when
you changed machines or could even break with just a new version of the
compiler.

> The key is to do one of three things.
>
[quoted text clipped - 17 lines]
>
> (3) is obviously irresponsible,

which is why I said it is the worst way to do it.

> but there's no general reason to prefer (1)
> over (2) or vice versa.   Of course, if you /do/ choose to use (2) then
[quoted text clipped - 5 lines]
> an implementation which looked like the above code on the 'C' end of the
> link.

I think there is a reason to prefer 1 over 2. If you chose 2 then you are
locking down how precise the number will be in the data format. What if we
later decide float is not precise enough and we want to use double on both
sides. Or perhaps we even go to BigDecimal. You then either have to throw
away that extra precision when talking between the two or change your
protocol. With choice1 it supports any arbitrary precision on either end.
You can certainly design a binary protocol (I don't actually find much
importance in the distinction between "text" and "binary") that allowed
variable precision but that would be a lot more work to implement.

Signature

Dale King

Chris Uppal - 13 Sep 2006 08:54 GMT
[me:]
> > but there's no general reason to prefer (1)
> > over (2) or vice versa. [...]
>
> I think there is a reason to prefer 1 over 2. If you chose 2 then you are
> locking down how precise the number will be in the data format.

Exactly the same thing happens with a textual representation.  If one end of
the communications link recieves a floating point value represented with more
digits of precision than is supported by its own floating-point representation,
then what is it to do ?  In general the only valid response would be to signal
some kind of error (though there may be certain /specific/ applications where
it would be understood that rounding/truncation is acceptable).  If you are
communicating numbers between computers then there is, explicitly or
implicitly, a fixed /range/ associated with each.  If the numbers are
floating-point then there is also, explicitly or implicitly, a fixed precision
associated.  Any protocol which fails to recognise that is broken.

   -- chris
Arne Vajhøj - 09 Sep 2006 03:11 GMT
>> listener= new ServerSocket(portNumber);
>> mySocket=listener.accept();
>> outp = new DataOutputStream(mySocket.getOutputStream());
>> outp.writeFloat(85.6f);

> I hope that was meant as a joke as it is the *WORST* way to do this. It is
> non-portable. You are assuming that Java and C use the same binary
> representation for their floating point values, which is a completely
> unfounded assumption.

Actually the Java code is not that bad.

It sends binary floats using standard IEEE floating
point format in standard network byte order.

Which is a very well defined format that almost
all non embedded computer will understand.

The problem is that the C code did not use ntohl.

> The most portable way to send the exact value is to do:
>
>   system.out.println( new BigDecimal( 85.6f ).toString() );

That constructor variant is general considered
bad practice.

Arne
EJP - 09 Sep 2006 06:18 GMT
> That constructor variant is general considered
> bad practice.

Why?
Arne Vajhøj - 13 Sep 2006 17:15 GMT
>> That constructor variant is general considered
>> bad practice.

Is in docs:

http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#BigDecimal(double)

"The results of this constructor can be somewhat unpredictable."

Just the usual floating point problem.

Arne
David Lee Lambert - 09 Sep 2006 22:43 GMT
> I have a Java class that opens a socket and connects with a client.  The
> client is written in C.
>
> All goes well, but when it comes time to write a floating point value the
> client has trouble reading it. [...]
 /* Java code ... */
> outp.println(85.6f);
 /* C code... */
> memset(buffer,'\0',sizeof(buffer));
> n = read(sockfd,buffer,127);
[quoted text clipped - 4 lines]
> value I send via the java server.  If I send an integer from the server, I
> have no problems using atoi() and printing it out.

I wrote some code (at http://www.lmert.com/download/gh.zip) that exhibits
a similar,  but not the same,  problem.  With my particular C library
(Debian, libc6 2.3.6-7) the atof() and strtod() functions both parse the
output of java.lang.Float.toString(),  but the strtof() function chokes.

IEEE 1003.1:2001 (which is supposed to be aligned with the ISO C standard)
says nothing about a maximum length that strtod() and strtof() can handle,
and basically says that atof() should be defined in terms of strtod().
Therefore the error you're seeing is probably a problem with your C
library, not with Java.  

On the other hand, if you're using a sufficiently recent version of java,
you could use outp.printf("%.8f", ...) instead.  Another easy workaround
should be to manually truncate the string to an interesting length,  in
either the client or the server or both.

Signature

PGP key posted on website ... http://www.lmert.com/people/davidl/

Simon Biber - 10 Sep 2006 08:20 GMT
>> I have a Java class that opens a socket and connects with a client.  The
>> client is written in C.
[quoted text clipped - 6 lines]
>> memset(buffer,'\0',sizeof(buffer));
>> n = read(sockfd,buffer,127);

println probably wrote ASCII text such as "85.6\n" to the socket.

Why are you reading 127 characters from the socket? You should read one
character at a time, until your reach the newline.

The first time you read 127 characters you probably included some of the
following data in what you read, which meant it was no longer available
next time you tried to read something.

>> flt=atof(buffer);
>> printf("You returned %lf from the server\n",flt);

Signature

Simon.

Martin Gregorie - 10 Sep 2006 11:40 GMT
> Why are you reading 127 characters from the socket? You should read one
> character at a time, until your reach the newline.

I prefer to use structured messages with a count as the first value,
something like "n,value" in this case, where 'n' is a fixed length
number, e.g. four digits zero filled on the left, that gives the length
of the "value" string + 1 for the comma.

This makes reading the message simple:

read 4 bytes (the length of the rest of the message)
convert to binary (lth)
read lth bytes into a value buffer
append a null character as the string terminator
skip the first byte (the comma) and the rest of the string is the value
that was sent.

You can transfer any combination of bytes with this method (including
binary values and UTF-16 encoded byte strings) but IMO you're better off
sending a plain ASCII string because:

- the ability to display the whole message without decoding it
  makes debugging a lot easier
- this allows the value to be retrieved with a single read (lower
  overhead than byte by byte reading)
- using formatted messages of this sort enables you to spot corrupted
  data and do something about it: in this case you could check that the
  comma is the first byte after the length.
- by sending an ASCII byte string you avoid big endian / little endian
  conflicts between hosts and other binary traps
- messages formatted this way are easy to assemble and parse in both
  Java and C

You must check that you've read the required number of bytes in case the
message got split during transmission. Remember that TCP/IP can split
messages up if buffer sizes differ along the route and that a split
message isn't necessarily recombined at the destination host.

Signature

martin@   | Martin Gregorie
gregorie. | Essex, UK
org       |



Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.