Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using SSE to round in Delphi

I wrote this function to round singles to integers:

function Round(const Val: Single): Integer;
begin
  asm
    cvtss2si eax,Val
    mov Result,eax
  end;
end;

It works, but I need to change the rounding mode. Apparently, per this, I need to set the MXCSR register.

How do I do this in Delphi?

The reason I am doing this in the first place is I need "away-from-zero" rounding (like in C#), which is not possible even via SetRoundingMode.

like image 958
IamIC Avatar asked Jan 10 '23 09:01

IamIC


1 Answers

On modern Delphi, to set MXCSR you can call SetMXCSR from the System unit. To read the current value use GetMXCSR.

Do beware that SetMXCSR, just like Set8087CW is not thread-safe. Despite my efforts to persuade Embarcadero to change this, it seems that this particular design flaw will remain with us forever.

On older versions of Delphi you use the LDMXCSR and STMXCSR opcodes. You might write your own versions like this:

function GetMXCSR: LongWord;
asm
  PUSH    EAX
  STMXCSR [ESP].DWord
  POP     EAX
end;

procedure SetMXCSR(NewMXCSR: LongWord);
//thread-safe version that does not abuse the global variable DefaultMXCSR
var
  MXCSR: LongWord;
asm
  AND     EAX, $FFC0 // Remove flag bits
  MOV     MXCSR, EAX
  LDMXCSR MXCSR
end;

These versions are thread-safe and I hope will compile and work on older Delphi versions.

Do note that using the name Round for your function is likely to cause a lot of confusion. I would advise that you do not do that.

Finally, I checked in the Intel documentation and both of the Intel floating point units (x87, SSE) offer just the rounding modes specified by the IEEE754 standard. They are:

  • Round to nearest (even)
  • Round down (toward −∞)
  • Round up (toward +∞)
  • Round toward zero (Truncate)

So, your desired rounding mode is not available.

like image 121
David Heffernan Avatar answered Jan 16 '23 01:01

David Heffernan