casting question

  • Thread starter Giorgos Keramidas
  • Start date
G

Giorgos Keramidas

Part of the OpenSolaris source contains the following macro[1]:

41 /*
42 * FRC2PCT macro is used to convert 16-bit binary fractions in the range
43 * 0.0 to 1.0 with binary point to the right of the high order bit
44 * (i.e. 1.0 == 0x8000) to percentage value.
45 */
46
47 #define FRC2PCT(pp) (((float)(pp))/0x8000*100)
48

which gets called with 16-bit values of `pp', i.e. from members of a
structure, as in the prstat.c[2] snipper shown below:

482 static void
483 list_update(list_t *list, lwp_info_t *lwp)
484 {
...
513 id->id_pctcpu += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);

But I recently discovered, by having a local program (which re-uses some
of the prstat Solaris source) crash when it reached that line from
list_update() that it's not always a good idea to blindly cast any
integer value to a float. Changing that macro to an inline function
that uses a `float' intermediate variable and re-arranging the
calculation a bit seems to have fixed the crash here:

static inline float
FRC2PCT(uint16_t pp)
{
float fval;

fval = (100.0 * pp) / 0x800;
return fval;
}

But I'm not sure why this breaks when the cast is used. Is casting an
integer value and then accessing it as a float something that causes UB
or even implementation-defined behavior?

- Giorgos

References:
***********

[1] http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/prstat/prstat.h
[2] http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/prstat/prstat.c
 
R

Rod Pemberton

Giorgos Keramidas said:
Part of the OpenSolaris source contains the following macro[1]:

41 /*
42 * FRC2PCT macro is used to convert 16-bit binary fractions in the range
43 * 0.0 to 1.0 with binary point to the right of the high order bit
44 * (i.e. 1.0 == 0x8000) to percentage value.
45 */
46
47 #define FRC2PCT(pp) (((float)(pp))/0x8000*100)
48

which gets called with 16-bit values of `pp', i.e. from members of a
structure, as in the prstat.c[2] snipper shown below:

482 static void
483 list_update(list_t *list, lwp_info_t *lwp)
484 {
...
513 id->id_pctcpu += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);

But I recently discovered, by having a local program (which re-uses some
of the prstat Solaris source) crash when it reached that line from
list_update() that it's not always a good idea to blindly cast any
integer value to a float. Changing that macro to an inline function
that uses a `float' intermediate variable and re-arranging the
calculation a bit seems to have fixed the crash here:

static inline float
FRC2PCT(uint16_t pp)
{
float fval;

fval = (100.0 * pp) / 0x800;

/* 0x800? Shouldn't that be 0x8000? You lost a zero. */

fval = (100.0 * pp) / 0x8000;

/* You may experience overflow errors if you multiply */
/* a uint16_t by 100 before dividing by 0x8000. */
/* There were two reason for that original cast: 1) was */
/* to increase the range available for the calculation */
/* and 2) was to properly calculate the "binary point." */
/* You haven't done _either_ in your calculation. pp _must_ */
/* be cast to a type with a larger range before the multiplication */
/* by 100 for the calculation to work properly. The calculation */
/* itself _must_ be cast to a float sometime prior to the division for */
/* the binary point to be calculated properly. */

fval = ((float)(100.0 * (uint32_t)pp)) / 0x8000;
return fval;
}

But I'm not sure why this breaks when the cast is used. Is casting an
integer value and then accessing it as a float something that causes UB
or even implementation-defined behavior?

References:
***********

[1] http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/prstat/prstat.h
[2] http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/prstat/prstat.c


Sorry, I don't know (or care) why it breaks...


Rod Pemberton
 
O

Old Wolf

Giorgos said:
#define FRC2PCT(pp) (((float)(pp))/0x8000*100)

id->id_pctcpu += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);

But I recently discovered, by having a local program (which re-uses some
of the prstat Solaris source) crash when it reached that line from
list_update() that it's not always a good idea to blindly cast any
integer value to a float.

Actually there is nothing wrong with that. If the integer cannot be
exactly represented in a float, then a neighbouring float value
will be used. Unless the integer is bigger than FLT_MAX in
which case you will get undefined behaviour. But this is
unlikely to be the case when using 16-bit ints.
Changing that macro to an inline function
that uses a `float' intermediate variable and re-arranging the
calculation a bit seems to have fixed the crash here:

static inline float
FRC2PCT(uint16_t pp)
{
float fval;

fval = (100.0 * pp) / 0x800;
return fval;
}

If you had written:

fval = ((float)pp) / 0x8000 * 100;

then this code would be the same as the macro. If one causes a
crash and the other does not, then you have a bug elsewhere in
your code (likely a memory allocation error or a buffer overflow)
that just happens to manifest itself in this way.

If your code as written works and the code I just suggested does
not, that would be bizarre. You could then try:

fval = ((double)pp) / 0x8000 * 100;

because floats are promoted to doubles in arithmetic expressions
anyway.
 
G

Giorgos Keramidas

Giorgos Keramidas said:
Part of the OpenSolaris source contains the following macro[1]:

41 /*
42 * FRC2PCT macro is used to convert 16-bit binary fractions in the range
43 * 0.0 to 1.0 with binary point to the right of the high order bit
44 * (i.e. 1.0 == 0x8000) to percentage value.
45 */
46
47 #define FRC2PCT(pp) (((float)(pp))/0x8000*100)
48

which gets called with 16-bit values of `pp', i.e. from members of a
structure, as in the prstat.c[2] snipper shown below:

482 static void
483 list_update(list_t *list, lwp_info_t *lwp)
484 {
...
513 id->id_pctcpu += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);

But I recently discovered, by having a local program (which re-uses some
of the prstat Solaris source) crash when it reached that line from
list_update() that it's not always a good idea to blindly cast any
integer value to a float. Changing that macro to an inline function
that uses a `float' intermediate variable and re-arranging the
calculation a bit seems to have fixed the crash here:

static inline float
FRC2PCT(uint16_t pp)
{
float fval;

fval = (100.0 * pp) / 0x800;

/* 0x800? Shouldn't that be 0x8000? You lost a zero. */

Yes, sorry about that. Copy/paste error :(
fval = (100.0 * pp) / 0x8000;

/* You may experience overflow errors if you multiply */
/* a uint16_t by 100 before dividing by 0x8000. */
/* There were two reason for that original cast: 1) was */
/* to increase the range available for the calculation */
/* and 2) was to properly calculate the "binary point." */
/* You haven't done _either_ in your calculation. pp _must_ */
/* be cast to a type with a larger range before the multiplication */
/* by 100 for the calculation to work properly. The calculation */
/* itself _must_ be cast to a float sometime prior to the division for */
/* the binary point to be calculated properly. */

fval = ((float)(100.0 * (uint32_t)pp)) / 0x8000;

All noted.
Sorry, I don't know (or care) why it breaks...

No problem. Thanks for all the help :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top