3

I want to produce a C# sqrt benchmark, but some sqrt functions require an union for bitwise computation.

My union is defined as it :

[StructLayout(LayoutKind.Explicit)]
struct U
{
    [FieldOffset(0)]
    public int i;
    [FieldOffset(0)]
    public float x;
}

and next code produces an unassigned field error on u.i :

U u;
u.x = x;
u.i = (1 << 29) + (u.i >> 1) - (1 << 22);

I know u.i was assigned when u.x is also assigned, so is it possible to ignore an unassigned field error at compile time without an explicit u.i assign?

Connell.O'Donnell
  • 3,603
  • 11
  • 27
  • 61
Florian J
  • 59
  • 8
  • 1
    No, you can't ignore. Just add it. However...why do you want to _emulate_ unions in C#?!?! Working with doubles you can use `BitConverter`. With floats...well for a benchmark I'd leave out the assignment and just calculate the `sqrt()`, an extra assignment for `u.i` won't be measured. If you really want to do it like that go to the slow way: http://stackoverflow.com/q/21801213/1207195 – Adriano Repetti Feb 22 '17 at 12:19
  • @AdrianoRepetti primary C function as an union i just want to replicate as C(ish) as possible. BitConverter seems slower than a basic 'emulated?' union but if it's wrong i'll use it. btw i'm agree for the extra assignment. – Florian J Feb 22 '17 at 13:17
  • Check linked post, in accepted answer there is `unsafe` memory access, should be as fast as C `union` – Adriano Repetti Feb 22 '17 at 14:07

1 Answers1

2

FieldOffset is primarily an interop feature; it tells how the runtime should marshal the structure when used in a native context. In some cases (blittable structures), it also affects the managed memory layout. It should be noted that any explicit structure layout will mean the code is no longer portable. That may or may not be a problem for your code.

The important part is that the compiler does not even try to verify unsafe code beyond a few very simple heuristics; it still sees two fields, and you're never assigning one of the fields, so you're violating the struct contract. This is not much different from using e.g. pointer arithmetic to access the field. It's still much more likely that you made a mistake and forgot to assign a field than that this is explicitly what you want. If you really want to, you can just do an assignment (or use a constructor; C# isn't C, and U u = new U(); is usually perfectly fine) before reading the field.

But in your case, there's little reason to use a union field anyway. If you want to do unmanaged operations like this, use unsafe code. That's what it's for. Don't abuse interop features. And of course, whichever way you choose, don't expect it to be portable.

float x = 42.0f;

(*(int*)((void*)&x)) // The value of `x` reinterpreted as an int

A full example of your sqrt approximation might look something like this:

unsafe void Main()
{
  sqrt(42).Dump(); // 6.625
  sqrt(9).Dump();  // 3.125
}

unsafe float sqrt(float x)
{
  int* pX = (int*)((void*)&x);

  *pX = (1 << 29) + (*pX >> 1) - (1 << 22);

  return x;
}
Luaan
  • 62,244
  • 7
  • 97
  • 116
  • i don't think it's correct to say "managed side would lay `i` over the same memory as `x`" an explicite struct layout is used to do interrop by bits positionning and in your case, this wouldn't work. Your `(int*)((void*)&x);` is translated to `(int*) &x` on IL code but you right it's seems faster than using struct, i'll bench both. Thanks @Luaan ! – Florian J Feb 22 '17 at 13:44
  • 1
    @FlorianJ I wrote you have no guarantee that would happen, i.e. the opposite of what you've read :) `StructLayout` (and `FieldOffset`) is for interop - any change they make to managed memory layout is an implementation detail, not something you can rely on. The same way, it's safe to take a pointer of a field, but not to add an offset to that pointer and expect to find a different field there - that simply isn't part of the contract. – Luaan Feb 22 '17 at 15:00
  • _FieldOffset is an interop feature, not something that should affect managed code in any way._ That's actually incorrect. `LayoutKind.Explicit` ... affects both managed and unmanaged layout, for both blittable and non-blittable types. – chase Aug 20 '19 at 07:28
  • @Luaan That was a direct quote from https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute – chase Aug 20 '19 at 07:55
  • ECMA-335: II.10.7 Controlling instance layout; II.16 Defining and referencing fields; II.22.16 FieldLayout. – chase Aug 20 '19 at 08:45
  • @chase See, it's actually *possible* to prove a positive :) That reference is all that I needed, I'm going to fix my answer now. – Luaan Aug 20 '19 at 10:14