0

I am analyzing a predecessor codes(codes run on microcontroller) handling pointing point, but I don't understand how things work. I have got to know how to convert flat to decimal and the other way around. However, what he did was used customized bit fields for exponent:6 bits and mantissa:26 bit in data structure.

typedef union {
  struct {
   #ifdef _BIG_ENDIAN
      unsigned int mant: 26;    /* -33,554,432 to +33,554,431 */
      unsigned int exp: 6;      /* 10^-32 to 10^+31 */
   #else
      unsigned int exp: 6;      /* 10^-32 to 10^+31 */
      unsigned int mant: 26;    /* -33,554,432 to +33,554,431 */
   #endif
 } part;
 unsigned long comp;
} DMKS;

As followed by programs logic in client side:

  1. A client(microcontroller) get data from a server and set a value to unsigned long comp

  2. Call M_to_u() which converts DMKS value to micro value

  3. M_to_u() looks like below:

    long M_to_u(DMKS dmks_val)
    {
    
    register unsigned int exp;
    long    retval;
    
    UARTprintf("before x= %x\n",  (dmks_val.comp));//First print of comp
    
    retval = (long) (dmks_val.part.mant);    
    
    UARTprintf("retval x= %x\n", (long) (dmks_val.part.mant));//second print of mantissa
    
    if (retval & 0xfe000000)
    {
        retval |= 0xfe000000;
    }
    
    exp = dmks_val.part.exp;
    
    UARTprintf("exp = %d\n", (long) (dmks_val.part.exp));//Third print of exponent
    //UARTprintf("exp x= %x\n", (long) (dmks_val.part.exp));
    
    
    switch(exp) {
      case 58:
    retval /= 1000000L;
    break;
      case 59:
    retval /= 100000L;
    break;
      case 60:
    retval /= 10000L;
    break;
      case 61:
    retval /= 1000L;
    break;
      case 62:
    retval /= 100L;
    break;
      case 63:
    retval /= 10L;
    break;
      case 0:
    break;
      case 1:
    retval *= 10L;
    break;
      case 2:
    retval *= 100L;
    break;
      case 3:
    retval *= 1000L;
    break;
      case 4:
    retval *= 10000L;
    break;
      default:
    break;
    }
    
    return(retval);
    

    }

  4. Print out values

-IEEE 754 standard specifies a binary32 format example

enter image description here

-customized floating point in this codes = 11110100001001000000111101

mantissa exponent +---+---+---+---+---+---+---+----+ | 11110100001001000000 | 111101 | +---+---+---+---+---+---+---+----+

  • 1st UARTPrint:: before x= 3d0903d, which is pure value of comp in union sent from server,and bin format = 11110100001001000000111101

  • 2nd print for mantissa fields:: retval x= f4240 which is extracted from bit fields of mantissa, and bin format = 11110100001001000000

  • 3rd print:: exp = 61 which is extracted from bit fields of exponent, and bin format = 111101

Questions are:

Unlike IEEE 754 standard specifies a binary32 (exp bits:23~30, and mantissa bits:0 ~22), customized bit fields are used for floating point in this codes.

1. How do things work? - This codes use exp bits:0~5 and mantissa bits:6~30, so client/server need to manipulate order of bits?

2. Why is it divided by 1000L? - print out showing exp value = 61, so get into case 61:, but how exponent 61 connects to divided by 1000L?

Thanks -Jin

timrau
  • 22,578
  • 4
  • 51
  • 64
Jin
  • 113
  • 11
  • 1
    Wow! OK, it looks to me like this is intended to be a floating-point format that thinks in decimal rather than binary. You pull the mantissa then manually decide on a decimal exponent (only in the -6..4 range, for some reason) and use that to scale by a factor of ten. Since you mention a server, you _also_ presumably have the `comp` alias so that you can call `htons()` to ship the beast across the network. Does that get you moving? One imagines (hopes, prays) that the server understands the same structure. I assume the guy didn't know that IEEE754 was an actual standard. – John C Mar 26 '14 at 20:02
  • Thanks for replying, the `comp` is set by `ntohs` before the above function is called. As for exponent, `2^(k−1) − 1` and k=6 in this codes, which gives me `exp=31`. So it should be 2^31 but predecessor commented `10^-32 to 10^+31` which is decimal base. I don't understand how this can be changed – Jin Mar 26 '14 at 20:55
  • That's what the `switch` statement is doing: It looks at the exponent and scales the mantissa _by powers of ten_...but only for a limited range. I'm not seeing the powers of two, though. Am I just dense or is that in a different code segment? If so, maybe that's a special _wider_ range. I'm curious: Is the server also in-house? If so, maybe there's some useful evidence there, and if not, maybe there's documentation. (At least the `union` makes sense, now.) – John C Mar 26 '14 at 23:33
  • `2^(k-1)-1` where k is nums of exp fields in floating point bits, which is the way get bias of exponent. I am just talking about normal way to get bias to convert decimal to floating point or the other way around. There are a couple of examples to show how to convert, which use `2^` not `10^`. I am still trying to figure out meaning of `10^` that predecessor commented on codes. – Jin Mar 27 '14 at 15:56
  • Yes, the server is also in-house which runs now. Unfortunately predecessors(both client and server) left almost nothing about documents, comments on me. I could take a look at server side codes, but it's painful looking at codes without documents and comments. Thank you for giving me an idea – Jin Mar 27 '14 at 16:04
  • My suspicion, since it's only the near-zero range, is that your predecessor decided it's easy to get that exponent range within the mantissa, and so the program "needs" a way to scale outside the normal exponent. It's just a guess, though. And again, I don't see `exp` used the way you're describing. I don't doubt you (you have the whole program in front of you, after all), but double-check that `2^(k-1)-1` part, because it's not in the code you posted. – John C Mar 27 '14 at 16:07
  • 1
    Right. And the `switch` statement also works in powers of ten, with a _manual sign_. 0 is x1. 1 is x10. 63 is -1 is /10. 2 is x100. 62 is -2 is /100. And so on, but only from -6 to 4, either because that's all that fits in a `long` (26 bits + 13 for x10000 is 39, so no) or because he assumed that was the maximum range. In other words, it _could be_ 10^-31 to 10^32, but 10^-6 to 10^4 was all that got implemented. – John C Mar 27 '14 at 19:15
  • Seems like no where to use 2^(k-1)-1 for getting bias. What I am trying to do is understand his codes especially switch statement. For instance, why `exp=61` needs to be divided by `1000L`. He seems like came up with the bias which is `31` in this codes by using `2^(6-1)-1` since I see his comment regarding `unsigned int exp: 6; /* 10^-32 to 10^+31 */`. – Jin Mar 27 '14 at 19:16
  • `exp` = 61(dec) as I printed out above. So logically we can come up with 61-31(bias) = 30. If binary base floating point, it turns to be `2^30` for exponent fields. but he seems like using `decimal` and in `switch`, he divides `mantissa` by `1000L`. I'm trying to decipher his logic by the normal way of converting which is binary base. But so far I can't get through his logic yet. Thanks – Jin Mar 27 '14 at 19:17
  • 1
    Oh, I'm sorry. I thought the sign part was obvious. It's not a "bias;" it's a negative number. A six-bit negative number counts 0 (000000) to 31 (011111), then 32 (100000) down to -1 (111111). So, 58 unsigned is -6 signed (111010), 59 is -5 (111011), 60 is -4 (111100), 61 is -3 (111101), 62 is -2 (111110), and 63 is -1 (111111). Make sense? – John C Mar 27 '14 at 19:31
  • Ohh, I see!! I was trapped by normal way of converting floating points used in IEEE standard which is http://en.wikipedia.org/wiki/Single-precision_floating-point_format. He just used simply decimal to sign converting to determine dividends. I really appreciate your help!! – Jin Mar 27 '14 at 20:42
  • I'm just sorry it took me so long to say it! – John C Mar 27 '14 at 22:23

1 Answers1

0

What your predecessor did may work, but it is an unreliable and dangerous approach -- the serialization order of bitfields within a struct is implementation-defined, and there is no requirement for it to be consistent with the endianness your CPU uses for integers (which, btw, is not necessarily the same as the endianness used for hardware floating point!), the order of declaration, or anything else.

Since you are receiving an unsigned long from the microcontroller in this custom format, I would make note of the order of fields -- it looks like the mantissa is in the high bits and the exponent is in the low bits, and the bit order within each field is consistent with the CPU's bit order; none of that can be relied on without documentation or testing -- and then use explicit bit masking to extract the fields, e.g.

unsigned long mantissa = (dmks_val & 0xFFFFFFC0) >> 6;
unsigned long exponent = (dmks_val & 0x0000003F) >> 0;

where dmks_val is now an unsigned long instead of the union. The union should be purged from your codebase with fire and sword.

I can't help you with question 2. Only someone who understands this wacky floating point format can help you with question 2. Incidentally, you'd better find the sign bit.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Sorry I just took out some codes in definition of union since what I am using is little endian. I added on those missing codes. Thanks – Jin Mar 26 '14 at 19:35
  • The "missing code" makes the very common but COMPLETELY INCORRECT assumption that the order of fields is correlated with the endianness of the CPU. Everything that I said is still true. – zwol Mar 26 '14 at 19:38
  • Thank you for answering. This codes have been doing service between client/server for a long time as I heard, and There isn't a designated person in charge of server side. I assume that this custom format is understandable for server. – Jin Mar 26 '14 at 21:27
  • You don't have to change the wire format, but you are right to think that the client-side code that processes it is broken and needs major revision. – zwol Mar 26 '14 at 21:28
  • So, this format is tightly coupled with between client and server. I am not sure I can take out `union`. To be honest, I don't understand `>>5` and `>>0` that you mentioned. thanks – Jin Mar 26 '14 at 21:29
  • I think you need to have a face-to-face conversation with someone at your company who knows how this wacky floating point format is supposed to work. If there is no such person that is in and of itself a major problem to bring up with management. I cannot help you any further because I don't know the format. – zwol Mar 26 '14 at 21:34
  • The `>>5` and `>>0` are bit shifts. If you've never seen them before please consult a good book on the C language; I can't fit a proper explanation into the comment box. – zwol Mar 26 '14 at 21:34
  • No one knows about the legacy codes. Every predecessor had gone before I got hired. What I am doing is implementing all legacy functions to new MCU. I know `>>` is bit shifts, but I don't get why you `right shift 5 bit` and `0` bit. Is it just an example using arbitrary numbers how to handle `exponent` and `mantissa`?;otherwise, they have a specific meaning in the above custom format?. – Jin Mar 26 '14 at 22:05
  • Those numbers are the correct choices for the specific format you described. – zwol Mar 26 '14 at 22:42
  • exponent bits:6 and mantissa:26, so what are you trying to do is filtering exact exp or mantissa values out of 32 bits. Is it correct?. if so, `unsigned long mantissa = (dmks_val & 0xFFFFFFc0) >> 6;` and `unsigned long exponent = (dmks_val & 0x0000003F) >> 0;` could be right. does it correct? – Jin Mar 26 '14 at 23:13
  • Oh! Yes, you're right. I misread the exponent as only 5 bits wide. – zwol Mar 26 '14 at 23:15