1

Does VB.Net support IEEE 754 half precision (16 bit) floating point data types? If not, is there a recommended way of creating them?

Context: I am ultimately sending a stream of bytes over a COM channel to a device that accepts half precision values. I need to take the Decimal value entered by the user and convert it to a two byte float for transmission. I am unsure the best way to do this in VB.Net

Toby
  • 9,696
  • 16
  • 68
  • 132
  • The smallest is `Single` (32bits) – shadow Jan 22 '16 at 17:27
  • 1
    Would be a duplicate of http://stackoverflow.com/q/10414889/11683 but that question is about fitting a float in two bytes in any way, not just according to IEEE. Still, it references http://sourceforge.net/projects/csharp-half/. – GSerg Jan 22 '16 at 17:52

1 Answers1

1

I'm not associated at all with this library, but you can use this.

It's a struct that uses internally an ushort member, which should be what you are transmitting. If you can't transmit the struct directly for some reason, you could just make the value field public, and use that (or use the GetBytes() method).

I'm unsure about the precision of the conversion though, you should experiment.

The library is C#, but there should be no problem adding it as a C# project to your solution and use it from Vb.Net

Update

I took the time and made a "safe" minimum version in Vb.net. All the code is from that library, all copyrights to him:

Public Class HalfPrecisionConverter
    Private Shared BaseTable As UShort() = GenerateBaseTable()
    Private Shared ShiftTable As SByte() = GenerateShiftTable()

    Private Shared Function GenerateBaseTable() As UShort()
        Dim intBaseTable = New UShort(511) {}
        For i = 0 To 255
            Dim e = CSByte(127 - i)
            If e > 24 Then
                ' Very small numbers map to zero
                intBaseTable(i Or &H0) = &H0
                intBaseTable(i Or &H100) = &H8000
            ElseIf e > 14 Then
                ' Small numbers map to denorms
                intBaseTable(i Or &H0) = CUShort(&H400 >> (18 + e))
                intBaseTable(i Or &H100) = CUShort((&H400 >> (18 + e)) Or &H8000)
            ElseIf e >= -15 Then
                ' Normal numbers just lose precision
                intBaseTable(i Or &H0) = CUShort((15 - e) << 10)
                intBaseTable(i Or &H100) = CUShort(((15 - e) << 10) Or &H8000)
            ElseIf e > -128 Then
                ' Large numbers map to Infinity
                intBaseTable(i Or &H0) = &H7c00
                intBaseTable(i Or &H100) = &Hfc00
            Else
                ' Infinity and NaN's stay Infinity and NaN's
                intBaseTable(i Or &H0) = &H7c00
                intBaseTable(i Or &H100) = &Hfc00
            End If
        Next

        Return intBaseTable
    End Function

    Private Shared Function GenerateShiftTable() As SByte()
        Dim intShiftTable = New SByte(511) {}
        For i = 0 To 255
            Dim e = CSByte(127 - i)
            If e > 24 Then
                ' Very small numbers map to zero
                intShiftTable(i Or &H0) = 24
                intShiftTable(i Or &H100) = 24
            ElseIf e > 14 Then
                ' Small numbers map to denorms
                intShiftTable(i Or &H0) = CSByte(e - 1)
                intShiftTable(i Or &H100) = CSByte(e - 1)
            ElseIf e >= -15 Then
                ' Normal numbers just lose precision
                intShiftTable(i Or &H0) = 13
                intShiftTable(i Or &H100) = 13
            ElseIf e > -128 Then
                ' Large numbers map to Infinity
                intShiftTable(i Or &H0) = 24
                intShiftTable(i Or &H100) = 24
            Else
                ' Infinity and NaN's stay Infinity and NaN's
                intShiftTable(i Or &H0) = 13
                intShiftTable(i Or &H100) = 13
            End If
        Next

        Return intShiftTable
    End Function

    Public Shared Function SingleToHalf([single] As Single) As UShort
        Dim value As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes([single]), 0)

        Dim result = CUShort(baseTable((value >> 23) And &H1ff) + ((value And &H7fffff) >> shiftTable(value >> 23)))
        Return result
    End Function
End Class

To use:

HalfPrecisionConverter.SingleToHalf(xx)

It returns a UShort, if you need the bytes to transmit, you can use BitConverter.GetBytes()

The C# unsafe version should be a bit faster since it does not use BitConverter and directly uses pointers to convert the single to a UInteger of its bytes, but I'm not sure VB.NET allows for any kind of pointer conversions

Jcl
  • 27,696
  • 5
  • 61
  • 92
  • 1
    VB does not have pointers, but you can [abuse a union](http://stackoverflow.com/a/6474800/11683). You don't have to though, because you can [send a `ushort`](https://msdn.microsoft.com/en-us/library/8sh9zw1e%28v=vs.110%29.aspx) down a stream just as well. Also I would advise to [use `select case`](http://stackoverflow.com/q/6540220/11683). – GSerg Jan 22 '16 at 18:53
  • @GSerg I'm no vb coder, just made the "safe" version in C# using the original library code (and changed to use `BitConverter` and ran it through an online code converter (and tested it on a project before submitting it here) – Jcl Jan 22 '16 at 18:55