0

I stumbled upon this (reduced for readability) function. It does weird things that I have never encountered before.

// a2 is always 60.0
void some_function(struct_123 *this, float a1, float a2)
{
  float v5; // ST10_4
  float v6; // ST14_4

  this->field_34 = a1;
  v5 = a2 / (0.011 * 1000.0); // 60.0 / 11.0
  v6 = v5 - (double)(signed int)v5;
  this->field_38 = (SLODWORD(v6) >> 31) + (signed int)v5;
}

I know a little bit about IEEE 754 decimals, so i think (SLODWORD(v6) >> 31) yields v6's sign bit. I suspect this is some kind of inlined floor/ceiling rounding operation, but I am anything but sure about this.

Edit: I stumbled upon the actual non-inlined method. Here it is:

int __cdecl float_sub_466560(float a1)
{
  float v1; // ST04_4

  v1 = (double)(signed int)a1 - a1;
  return (signed int)a1 - (SLODWORD(v1) >> 31);
}

Edit 2: it seems like the first function I gave adds 1 in the last line. That is part of the function, not the floating point operation. I removed it.

Edit 3: as requested, here is the assembly for the second function. I can provide the assembly from the first function, however it is quite big, and I don't really want to extract the right portion of it if it's not necessary.

float_sub_466560 proc near

var_8           = dword ptr -8
var_4           = dword ptr -4
arg_0           = dword ptr  4

                sub     esp, 8          ; stack frame
                fld     [esp+8+arg_0]   ; Load Real
                fist    [esp+8+var_8]   ; Store Integer
                fisubr  [esp+8+var_8]   ; Subtract Integer Reversed
                fstp    [esp+8+var_4]   ; Store Real and Pop
                mov     eax, [esp+8+var_4]
                mov     ecx, [esp+8+var_8]
                sar     eax, 1Fh        ; Shift Arithmetic Right
                sub     ecx, eax        ; Integer Subtraction
                mov     eax, ecx        ; move result to correct return register
                add     esp, 8          ; stack frame
                retn                    ; Return Near from Procedure
float_sub_466560 endp
0

I have added some comments to the disassembly you posted, so it's easier to understand.

sub     esp, 8          ; stack frame
fld     [esp+8+arg_0]   ; load value of arg0 to st
fist    [esp+8+var_8]   ; store round(arg0) in var8
fisubr  [esp+8+var_8]   ; subtract arg0 from var8
fstp    [esp+8+var_4]   ; store result in var4
;now var4 = round(arg0) - arg0
mov     eax, [esp+8+var_4] ; eax = round(arg0) - arg0
mov     ecx, [esp+8+var_8] ; ecx = round(arg0)
sar     eax, 1Fh        ; shift eax right by 31; eax contains just a sign bit
sub     ecx, eax        ; if eax was negative, subtract 1, do nothing otherwise
mov     eax, ecx        ; eax = round(arg0) - signbit(round(arg0) - arg0)
add     esp, 8          ; stack frame
retn                    ; Return Near from Procedure

where round(n) can be a function:

  • rounding to the nearest integer
  • rounding down (floor)
  • rounding up (ceil)
  • rounding toward 0 thus returning the integer part of n,

depending on the rounding mode.

  • I looked at the documentation for the fist instruction - and according to that, it actually rounds according to the current rounding mode. The default for that is "Round to nearest", so am I right to assume that if arg0's value was 1.6, var8 would contain 2, but if it was 1.4, var8 would contain 1? – Fridtjof Mund Jul 7 at 23:21
  • You are right. If we assume that this is the rounding mode, fist will just round to the nearest integer value. Information about the rounding mode will be containted in RC field so you can check its value when debugging application. See xem.github.io/minix86/manual/intel-x86-and-64-manual-vol1/… – bart1e Jul 8 at 5:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.