1

Is it safe to assume that sizeof(double) will always be greater than or equal to sizeof(void*)?

To put this in some context, is the following portable?

int x = 100;
double tmp;

union {
  double dbl;
  void* ptr;
} conv;

conv.ptr = (void*)&x;
tmp = conv.dbl;

conv.dbl = tmp;
printf("%d\n", *((int*)conv.ptr));

It does work on the few machines that I've tested it on, but I can see this going horribly wrong if sizeof(void*) > sizeof(double).

Shawn Chin
  • 84,080
  • 19
  • 162
  • 191
  • It is ub AFAIK since you are writing to one union member and reading from another. – RedX Jul 03 '12 at 10:46
  • 4
    No, it's not safe to assume that. Why shouldn't a `void*` be 16 bytes, for example? Nothing forbids that. – Daniel Fischer Jul 03 '12 at 10:46
  • @RedX In C11, it is no longer UB. Was before, of course. – Daniel Fischer Jul 03 '12 at 10:47
  • I'm interested in this question too. Obviously it's not guaranteed by the standard, but are there (or are there likely to ever be) actual implementations where sizeof(void*) > sizeof(double)? – Thomas Padron-McCarthy Jul 03 '12 at 10:51
  • 1
    @DanielFischer That's what I suspected, but I can't seem to find a relevant reference explicitly stating that. I'm looking for something I can use to beat senseless the author of the above code. – Shawn Chin Jul 03 '12 at 10:56
  • When we reach past the "16 EB ought to be enough for anybody", pointer size might grow past the size of `double` :) – Hristo Iliev Jul 03 '12 at 11:16
  • 4
    I think AS-400 virtual instruction set has 128-bit pointers. – n. m. could be an AI Jul 03 '12 at 11:16
  • @Daniel, it never has been undefined behavior to do this. There had been some vagueness in the description before the correction (pre C11 btw), but the intention was never to make this UB. This is only UB if the data that you access happens to be a trap representation of the type through which you access it. – Jens Gustedt Jul 03 '12 at 11:27
  • I thought type punning was *implementation defined*, not undefined. – detly Jul 03 '12 at 11:32
  • 1
    @ThomasPadron-McCarthy -- Yes. IBM iSeries -- 16 byte pointers. – Hot Licks Jul 03 '12 at 11:36
  • 1
    On AS/400 / iSeries the above manipulation would cause the pointer to become "untagged" and invalid. – Hot Licks Jul 03 '12 at 11:38
  • @JensGustedt -- It is undefined behavior to modify a part of a variable using a pointer, vs modifying the entire variable. To do so relies on the details of the given machine. – Hot Licks Jul 03 '12 at 11:41
  • @HotLicks “If the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning").” C99TC3, footnote 82. – Pascal Cuoq Jul 03 '12 at 18:01
  • @PascalCuoq -- That doesn't say that what you get is valid. – Hot Licks Jul 03 '12 at 18:26

2 Answers2

3

On current systems yes. double is 64 bits on all current and future systems because they're aligned with IEEE arithmetic's double-precision. It's unlikely but certainly possible that pointers could be larger in the future - probably not for the sake of larger address space, but instead for carrying with them bounding information.

In any case it seems like a really bad idea to rely on any relationship between double and void *...

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
1

The size has nothing to do with it. There will always be some bits stored in there and the size will always be large enough to hold a void*. What will go wrong is that you interpret an almost random bit pattern as a pointer, this can't do much else than crash, but most probably you knew that already. Don't do it.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 2
    All he uses the `double` for is to move bits around. The actual data starts as `int *`, and is eventually interpreted as `int *`. But he's afraid that passing it through `double` will lose some of the pointer's bits. If `sizeof(double)>=sizeof(void *)`, no loss should happen. – ugoren Jul 03 '12 at 13:39
  • Note that simply loading/storing a double may alter its bit pattern. – Hot Licks Jul 03 '12 at 18:25
  • @HotLicks: If we're talking about IEEE arithmetic, I don't think that can happen. All `double` representations are valid (either as normal or denormal numbers, infinities, or NANs). Each non-NAN has a unique representation (so loading and storing it can't change the representation or it would also change the value) and NANs are purposefully preserved (even in arithmetic and transcendental functions, when possible) per IEEE to allow them to carry additional information as the programmer sees fit. – R.. GitHub STOP HELPING ICE Jul 04 '12 at 01:44
  • I'm thinking there's one case -- denormalized zero, maybe it is -- where the bit pattern is allowed to be changed by load/store. And several alterations are possible if even a hint of an actual "operation" is performed on the value. – Hot Licks Jul 04 '12 at 03:35
  • Zero is always "denormal"; the only "multiple zero representations" are positive and negative zero, and they are not changed by load/store; they're distinct values but can be changed by some "trivial" operations like adding zero. Note that there are even some operations you can safely perform bijectively on doubles, such as unary minus. – R.. GitHub STOP HELPING ICE Jul 05 '12 at 11:08