Optimize conversion of Microsoft Basic float to IEEE float

I was wondering if anyone has some thoughts on how the following algorithm can be optimized to run faster.

The method converts a Microsoft float (stored at an arbitrary position in a MemoryBlock) to an IEEE float format. The method is called a gazillion times, so even the slightest performance improvement will make a huge difference.

Private Function convertFloat(mb As MemoryBlock, pos As Integer) As Single
  ' convert Microsoft Basic floating point format to IEEE floating point format
  
  Dim result As Single
  Dim exp As Byte
  Dim ieee As new MemoryBlock(4)
  
  result = 0
  
  if pos < mb.Size then
    
    if mb.UInt32Value(pos) <> 0 then
      
      exp = mb.Byte(pos + 3) - 2
      ieee.Byte(3) = (mb.Byte(pos + 2) and &H80) or (exp \\ 2)
      ieee.Byte(2) = ((exp * 128) and &HFF) or (mb.Byte(pos + 2) and &H7F)
      ieee.Byte(1) = mb.Byte(pos + 1)
      ieee.Byte(0) = mb.Byte(pos + 0)
      
      result = ieee.SingleValue(0)
      
    end if
    
  end if
  
  return result
  
End Function

Thank you in advance.

you could reuse your memoryblock…

Static ieee As new MemoryBlock(4)
rather than

dim ieee as new MemoryBlock(4)

went from 66 ticks to 24 ticks in my very brief test for 1000000 calls…

Thanks, changed it to static and it now runs in 0.0135ms instead of 0.0142ms. Already a noticeable difference in the overall running of the app.

There is a dll by Microsoft which you can download which will do the conversion. I don’t now how fast they are, but it might be worth a try. Of course you will have to use declares.

Converting MBF to IEEE in Visual Basic for Windows

Thanks Eli. I will look into this.

Further optimize by combining the if/then’s into one statement and drop the result variable (just return the result rather than assigning it).

[code]Private Function convertFloat(mb As MemoryBlock, pos As Integer) As Single
’ convert Microsoft Basic floating point format to IEEE floating point format

Dim exp As Byte
Static ieee As new MemoryBlock(4)

if pos < mb.Size and mb.UInt32Value(pos) <> 0 then

exp = mb.Byte(pos + 3) - 2
ieee.Byte(3) = (mb.Byte(pos + 2) and &H80) or (exp \\ 2)
ieee.Byte(2) = ((exp * 128) and &HFF) or (mb.Byte(pos + 2) and &H7F)
ieee.Byte(1) = mb.Byte(pos + 1)
ieee.Byte(0) = mb.Byte(pos + 0)

return ieee.SingleValue(0)

end if

return 0

End Function
[/code]

Nice… down to 0.0132ms. Thanks Jim.

So far I’m getting the best times with this algorithm:

  • Now accessing data via pointers instead of MemoryBlock methods
  • Removed check to see if pos is within bounds
  • Replaced last two ieee.Byte assignments with one UInt16 assignment
Private Function convertFloat(mbPtr As Ptr, pos As Integer) As Single
  ' convert Microsoft Basic floating point format to IEEE floating point format
  
  Dim exp As Byte
  Static ieee As new MemoryBlock(4)
  Static ieeePtr As Ptr = ieee
  
  if (mbPtr.UInt32(pos) <> 0) then
    
    exp = mbPtr.Byte(pos + 3) - 2
    ieeePtr.Byte(3) = (mbPtr.Byte(pos + 2) and &H80) or (exp \\ 2)
    ieeePtr.Byte(2) = ((exp * 128) and &HFF) or (mbPtr.Byte(pos + 2) and &H7F)
    ieeePtr.UInt16(0) = mbPtr.UInt16(pos)
    
    return ieeePtr.Single(0)
    
  end if
  
  return 0
  
End Function

You might try some pragmas too but I don’t know if any will really help. This is my standard pragma speedup block…

#pragma DisableBackgroundTasks #pragma DisableBoundsChecking #pragma StackOverflowChecking false #pragma NilObjectChecking false
I think only StackOverflow and BackgroundTasks may have an effect here.

If your input memory block has arrays of floats it might help to also make an array version of the method instead of calling it for each element

Private Function convertFloat(mbPtr As Ptr, pos As Integer, size As integer) As Singleb[/b]

And I see this expression twice: mbPtr.Byte(pos + 2). Precomputing that might help the tensest bit, though with only 2 uses it might not.

I’ve already tried those pragmas but they had no effect.

Also tried to precompute mbPtr.Byte(pos + 2), but it made no difference.

I’ll try the pragmas in the parent method, that calls convertFloat(), and see if that perhaps makes a difference. Will also check if it’s possible to make an array version of the method.

Thanks for the tips Will.

[quote=191811:@Alwyn Bester]I’ve already tried those pragmas but they had no effect.

Also tried to precompute mbPtr.Byte(pos + 2), but it made no difference.

I’ll try the pragmas in the parent method, that calls convertFloat(), and see if that perhaps makes a difference. Will also check if it’s possible to make an array version of the method.

Thanks for the tips Will.[/quote]

You may want to use XojoScript. In previous benchmarks, it showed a drastic improvement in calculation speed over regular code.
https://forum.xojo.com/14123-xojo-benchmark/0

For zeroes, you may wish not to call the function at all

if (mbPtr.UInt32(pos) = 0) then //use 0 else retval = convertFloat(mbPtr As Ptr, pos As Integer) end if

You’re breaking down a UINT32 into bytes and working on them.
You could pass in a UINT32 and put that in a memoryblock, then access the bytes directly without the Pos+2 , pos+3 math…

if (mbPtr.UInt32(pos) = 0) then //use 0 else retval = convertFloat(mb.uint32(pos)) end if

[code]
Private Function convertFloat(mb as uint32) As Single
’ convert Microsoft Basic floating point format to IEEE floating point format

Dim exp As Byte
Static ieee As new MemoryBlock(4)
Static ieeePtr As Ptr = ieee
static TargetMB as new Memoryblock(4)
TargetMB.UINT32(0) = mb

exp = TargetMB.Byte(3) - 2
ieeePtr.Byte(3) = (TargetMB.Byte(2) and &H80) or (exp \\ 2)
ieeePtr.Byte(2) = ((exp * 128) and &HFF) or (TargetMB.Byte( 2) and &H7F)
ieeePtr.UInt16(0) = TargetMB.UInt16(pos)

return ieeePtr.Single(0)

End Function[/code]

Thanks for all the advice. I’m going to try it as soon as I’m back at my machine, and will post some feedback on the results.