-3

I want do the two's complement of a float data.

    unsigned long Temperature ; 
    Temperature = (~(unsigned long)(564.48))+1;

But the problem is that the cast loses information, 564 instead of 564.48. Can i do the two's complement without a loss of information?

physics
  • 161
  • 9
  • 4
    `564.48` is a `double`, casting to `unsigned long` will truncate it to `564`. What do you expect? – Yu Hao Sep 02 '14 at 08:37
  • memcpy float value to unsigned long and then apply two's component. Or use union with float and unsigned long, assign 564.48f to float, and apply two's component to unsigned long. – Alex F Sep 02 '14 at 08:40
  • @AlexFarber: These will all exhibit UB. – bitmask Sep 02 '14 at 08:48
  • 1
    What do you actually need to do? Performing two's complement on floating point data makes no sense, it will not be useful in any manner. – interjay Sep 02 '14 at 08:50
  • 1
    do you know that there is no temperature scala where `-564,48` is a valid value? – mch Sep 02 '14 at 08:54
  • Yes it is a valid value. Because i do it after sign reversal. The purpose of the two's comlement is to transmitt this value via Arinc. – physics Sep 02 '14 at 09:07
  • @mch `~` is different to `-` – M.M Sep 02 '14 at 09:12
  • @bitmask: assuming that OP knows exactly what he does, and needs to apply bitwise operation on float, as on integer, this is what he needs to do. It looks like he needs this for communication, this makes sense. – Alex F Sep 02 '14 at 09:28
  • 1
    @bitmask: type punning through memcpy and unions is well-defined (barring trap representations); reading from a union member you have not written to was wrongly listed as unspecified behaviour in the (non-normative) annex to C99; this has been corrected with C11 – Christoph Sep 02 '14 at 14:30
  • @Christoph: Interesting. Didn't know that. Apparently that's one more difference between C and C++. Thanks for the information! – bitmask Sep 02 '14 at 14:49

3 Answers3

1

That is a very weird thing to do; floating-point numbers are not stored as 2s complement, so it doesn't make a lot of sense.

Anyway, you can perhaps use the good old union trick:

union {
  float real;
  unsigned long integer;
} tmp = { 564.48 };

tmp.integer = ~tmp.integer + 1;
printf("I got %f\n", tmp.real);

When I tried it (on ideone) it printed:

I got -0.007412

Note that this relies on unspecified behavior, so it's possible it might break if your compiler does not implement the access in the most straight-forward manner. This is distinct form undefined behavior (which would make the code invalid), but still not optimal. Someone did tell me that newer standards make it clearer, but I've not found an exact reference so ... consider yourself warned.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • Not sure on where you got `it's safe in modern compilers` from? – tangrs Sep 02 '14 at 08:50
  • @tangrs Me neither ... I rewrote it to make it clearer. – unwind Sep 02 '14 at 08:57
  • Maybe I am wrong but seems that OP wants `(~tmp.integer) + 1` instead of `~tmp.integer + 1` – David Ranieri Sep 02 '14 at 10:06
  • @AlterMann Those expressions are exactly equivalent, `~` [has higher precedence](http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence) than `+`. – unwind Sep 02 '14 at 10:07
  • @unwind, not in this case, `tmp.integer = ~tmp.integer;` and `tmp.integer = ~tmp.integer + 1;` produces the same result: http://ideone.com/siXo94, the result must be `0.992588` if you add 1 after the conversion – David Ranieri Sep 02 '14 at 10:30
  • @AlterMann What? That `1` is being added in the integer expression, it can't transform `-0.007412` to `0.992588`? It's just adding a very insignificant bit to the "fraction" part of the float (assuming the same bit-order for floats as for integers). I tried adding `10000` instead, that changes the `float` value to `-0.007417`. – unwind Sep 02 '14 at 10:43
  • I mean `~564.48 = -0.007412` (your result), and OP wants `-0.007412 + 1` (`0.992588`), to make it clearer, you don't compute the `+1`: take a look: http://ideone.com/gnjMDx – David Ranieri Sep 02 '14 at 10:47
  • why don't just use `tmp.integer = -tmp.integer`? – phuclv Sep 02 '14 at 10:53
  • @LưuVĩnhPhúc Because that would require the machine to use 2s complement, which is not guaranteed. – unwind Sep 02 '14 at 10:54
  • I don't think the OP's machine doesn't use 2's complement except if he's using some ancient machine – phuclv Sep 02 '14 at 11:07
1

You can't use ~ over floats (it must be an integer type):

#include <stdio.h>

void print_binary(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char *) ptr;
    unsigned char byte;
    int i, j;

    for (i = size - 1; i >= 0; i--) {
        for (j = 7; j >= 0; j--) {
            byte = b[i] & (1 << j);
            byte >>= j;
            printf("%u", byte);
        }
    }
    printf("\n");
}

int main(void)
{
    float f = 564.48f;
    char *p = (char *)&f;
    size_t i;

    print_binary(sizeof(f), &f);
    for (i = 0; i < sizeof(float); i++) {
        p[i] = ~p[i];
    }
    print_binary(sizeof(f), &f);
    f += 1.f;
    return 0;
}

Output:

01000100000011010001111010111000
10111011111100101110000101000111

Of course print_binary is there for test the result, remove it, and (as pointed out by @barakmanos) print_binary assumes little endian, the rest of the code is not affected by endiannes:

#include <stdio.h>

int main(void)
{
    float f = 564.48f;
    char *p = (char *)&f;
    size_t i;

    for (i = 0; i < sizeof(float); i++) {
        p[i] = ~p[i];
    }
    f += 1.f;
    return 0;
}
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • Will yield different results on big-endian and little-endian. – barak manos Sep 02 '14 at 09:24
  • @barakmanos, are you sure? Endianess only affects byte order. Since `char` is exactly one byte it is the same on all endianess formats. [Take a look](http://stackoverflow.com/q/21761952/1606345) – David Ranieri Sep 02 '14 at 09:30
  • 1
    Yes, but you are casting a `float*` to a `char*` when you call `print_binary`. – barak manos Sep 02 '14 at 09:34
  • BTW, I didn't say it was wrong, I implied that it was worth mentioning as part of the answer... – barak manos Sep 02 '14 at 09:35
  • @barakmanos, ah ok, you are talking about the `print_binary` part, you are right, assumes little endian. – David Ranieri Sep 02 '14 at 09:40
  • shouldn't you use `unsigned char` also in main instead of `char`? if `char` is signed, it is not an exception of the strict aliasing rule. – mch Sep 02 '14 at 09:46
  • 1
    Thanks... For a minute I thought that the `for` loop (where you actually change the floating-point value) was also subjected to the endianess of the underlying HW architecture, but then I realized that you were doing it for each byte separately. – barak manos Sep 02 '14 at 09:47
  • @mch, signedness makes no difference to alias rules, you can use both `char *` and `unsigned char *` – David Ranieri Sep 02 '14 at 09:52
0

Casting a floating-point value to an integer value changes the "bit contents" of that value.

In order to perform two's complement on the "bit contents" of a floating-point value:

float f = 564.48f;
unsigned long Temperature = ~*(unsigned long*)&f+1;

Make sure that sizeof(long) == sizeof(float), or use double instead of float.

barak manos
  • 29,648
  • 10
  • 62
  • 114
  • 2
    Performing two's complement on the bit contents of a float is a completely meaningless operation. – interjay Sep 02 '14 at 08:48
  • @interjay: That's well-worth mentioning. I thought about it, but figured that OP might have his/her reasons (possibly need to write it into a register, or store it in some binary data-base, etc). – barak manos Sep 02 '14 at 08:55
  • in C++ this is UB because of the strict aliasing rule. I’m not sure about C, I think it is OK in C, can you confirm? – bolov Sep 02 '14 at 09:12
  • @bolov It can break on C too. It's better to use `memcpy` instead. – user694733 Sep 02 '14 at 09:20
  • @bolov: I'm not really sure how to confirm this. I'd expect that if both types are the same size, then there is nothing to prevent this from being executed successfully, i.e., without a segmentation fault, and with a deterministic result (different on LE and BE of course, as I've mentioned at the bottom of the answer). – barak manos Sep 02 '14 at 09:20
  • @user694733: Is that due to the address-alignment of `float`? – barak manos Sep 02 '14 at 09:22
  • @barakmanos Possibly. If `sizof` both types is the same, they usually have same alignment too. But there are no guarantees, and that alone is reason to not use it. – user694733 Sep 02 '14 at 09:26