4

In C#, I write

unsafe void Main() {
  float f = 3.14f;
  int i = *(int*)&f;
}

Is it possible to translate this code to F#?

My understanding is that a pointer is represented by the nativeptr<'a> type, but I for the life of me I cannot find an equivalent of the address reference & and pointer dereference * operators. I suspect NativePtr.get may be the latter, but its implementation escapes me, as I do not understand IL well enough.

I know about BitConverter and Marshal, but am looking for a way to implement bit mangling without copying memory.

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
  • @GuyCoder: I want to look at the *bits* of an existing float as if it was an int. – kkm inactive - support strike Mar 22 '16 at 22:54
  • What is the purpose of this? It is almost certainly a bad idea to write this sort of code. – John Palmer Mar 22 '16 at 23:01
  • @JohnPalmer: It is totally irrelevant to the question, but the intention is hashing. – kkm inactive - support strike Mar 22 '16 at 23:10
  • 4
    @GuyCoder: Could you pretty please refrain from personal attacks on my intentions, especially without knowing them up front? – kkm inactive - support strike Mar 22 '16 at 23:13
  • 1
    For nativeptr ops see https://msdn.microsoft.com/en-us/library/ee340434.aspx – John Palmer Mar 22 '16 at 23:20
  • I am not attacking I am agreeing. I did a prototype of a disassembler in F# so this is familiar. I remember having a similar problem, but I think I finally had to go with marshalling to get past the type checking. – Guy Coder Mar 22 '16 at 23:22
  • @JohmPalmer: Thanks, and this is exactly the class that I am referencing in my own question. So is `NativePtr.get` the right substitution for C# `*` when `'T` is a primitive value type? I do not really have an idea what `ldobj` is. – kkm inactive - support strike Mar 22 '16 at 23:24
  • @GuyCoder: Sorry, I must have misread you then, no offence taken Yes, sounds weird, but I need to bit-hash some internal collections. in `Bitconverter` there is `DoubleToInt64Bits` (which I do not need) but no `SingleToInt32Bits` (which I need, and which is no more than [`*((int*)&value`](https://github.com/mgravell/coreclr/blob/master/src/mscorlib/src/System/BitConverter.cs#L471). It would be even sillier to may taste to create a separate C# library just of this single function! – kkm inactive - support strike Mar 22 '16 at 23:31
  • I don't know what `ldobj` does either. You can always test this with integers / bytes / something easy or even write a comparison program to compare with C. Having said that, `get` is pretty designed to pretend that the ptr points to an array. – John Palmer Mar 22 '16 at 23:32
  • @JohnPalmer: Yes, indeed I could test it, but at this moment I have not found candidate for the other operation, the `&`. Without it, I can only shoot random addresses with `NativePtr.get` :) – kkm inactive - support strike Mar 22 '16 at 23:33
  • @GuyCoder: Interesting approach, thanks. That would practically solve *my original problem* (I think the JIT compiler even optimize out extra value copy on the stack in constructing this object, so there will unlikely be even a single cycle performance hit), but the question about F#'s equivalents of `&` and `*` still a different one. So no, not a duplicate. – kkm inactive - support strike Mar 22 '16 at 23:55
  • Of interest: [How to create a Union type in F# that is a value type?](http://stackoverflow.com/questions/31405194/how-to-create-a-union-type-in-f-that-is-a-value-type) – Guy Coder Mar 23 '16 at 00:28
  • .NET has official API for that now https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe/ – Dzmitry Lahoda Oct 01 '18 at 13:19

1 Answers1

11

The NativePtr.get and set functions read and write at an offset. If you need to read byte by byte, then use that. If you need to read at offset zero, you can use read and write instead, they will be slightly more performant.

The operator for taking "raw" address (as opposed to a byref<_> reference) is called && (see definition).

But there is some more trickery involved, such as: you need to mark the variable mutable before you can take its address, you can't just store a nativeptr<_> value, you need to convert it to nativeint, plus the nativeptr<_> values are strongly typed, so you need to convert between them via nativeint, etc.

The following snippet will do the equivalent of your C# code (step by step and with full type annotations for increased clarity):

open FSharp.NativeInterop

let Main() =
  let mutable x: float = 3.1415
  let floatPtr: nativeint = NativePtr.toNativeInt<float> &&x
  let intPtr: nativeptr<int> = floatPtr |> NativePtr.ofNativeInt<int>
  let asInt: int = NativeInterop.NativePtr.read intPtr
  asInt

Or a more compact version:

open FSharp.NativeInterop

let Main() =
  let mutable x = 3.1415
  &&x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read<int>

Or package it for reuse:

// val inline readAs : x:'a -> 'b when 'a : unmanaged and 'b : unmanaged
let inline readAs (x: 'a) : 'b =
  let mutable x' = x
  &&x' |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read<'b>

let Main() =
  let i = readAs 3.1415 : int
  ()

Having said all the above, I completely agree with John Palmer and GuyCoder: please don't do this if at all possible. This looks like exactly the kind of premature optimization that Dr. Knuth warned us about.

Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • I agree that one should not do this if they don't know what they are doing. However, there are very good reasons to do it if you know what you are doing and need it. If you do need pointers then look at [SafeHandles](https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle(v=vs.110).aspx). If you need to do lots of low level manipulation the look at the [System.Runtime.InteropServices](https://msdn.microsoft.com/en-us/library/system.runtime.interopservices(v=vs.100).aspx) – Guy Coder Mar 22 '16 at 23:59
  • 1
    This is it, thank you! Very ugly but does the trick. As for why I need that, I am copying my comment from above: “[...]I need to bit-hash some internal collections. In `BitConverter` there is `DoubleToInt64Bits` (which I do not need) but no `SingleToInt32Bits` (which I need, and which is no more than `*((int*)&value`. It would be even sillier to may taste to create a separate C# library just of this single function!” Indeed, I love things functional and pure, but sometimes those dirty bits still intrude. – kkm inactive - support strike Mar 23 '16 at 00:24
  • Oh by the way, is the use of `inline` really warranted here, and why? There are no "caret" static types in the declaration (`^T` as opposed to `'T`). Am I missing another use case for the `inline` keyword? – kkm inactive - support strike Mar 23 '16 at 00:53
  • No, `inline` is not strictly speaking necessary. However, if you're after performance, extra function call is a big deal. I have erroneously assumed you were after performance, because you were asking for pointer arithmetic. – Fyodor Soikin Mar 23 '16 at 01:22
  • 3
    Note that C#'s `float` is called `float32` in F#. – kvb Mar 23 '16 at 02:00
  • @FyodorSoikin, thank you. I actually am, and this is perhaps another user case. Thanks! – kkm inactive - support strike Mar 24 '16 at 20:19
  • Can you pls explain me what is x' and 'x I'm new to F#. – Hasan A Yousef Feb 02 '18 at 16:25
  • @HassanAYousef `x'` is just an identifier. Tick (aka "prime") can be part of identifiers in F#, just like numbers and underscore. `'x` is a generic type parameter. – Fyodor Soikin Feb 02 '18 at 17:28