.NET implementation: double conversion to Math.Min (float, float)

Why .NET implements the Math.Min (float, float) function as follows:

public static float Min(float val1, float val2) { if ((double) val1 < (double) val2 || float.IsNaN(val1)) return val1; else return val2; } 

While I see the use of IsNaN, I don’t understand why they are converted to double when comparing values. Isn't that so slower than just writing val < val 2 ? Especially if I wanted to use it to fix the value for numbers without much precision, like 0f or 1f .

Should I just go ahead and implement a custom math library that also additionally requires non-NaN values ​​to get maximum performance or is a waste of time?

 public static float Min(float a, float b) { return a < b ? a : b; } 
+4
source share
4 answers

Yes, I used Resharper, which does just that.

And what a problem, it distorts the code. The source code is available from the Help source , it looks like this:

  [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static float Min(float val1, float val2) { if (val1 < val2) return val1; if (Single.IsNaN(val1)) return val1; return val2; } 

It's hard to guess why Resharper does this, I would readily make it just a mistake. Thank the source code available from Microsoft, not only for accuracy, but also nice comments.

+5
source

I can’t tell you why they chose to compare float - double for comparison, but I doubt that this will cause any noticeable or significant effect of speed on your program. I would just use the built-in function.

+2
source

There is no such thing as Math.Min (float, float) .. NET contains only the definition for Min (double, double). Check MSDN: http://msdn.microsoft.com/en-us/library/system.math.min(v=vs.100).aspx

EDIT: yes, thanks @gideon http://msdn.microsoft.com/en-us/library/070xee48.aspx

+1
source

In general, you cannot assume that Single performs faster than Double . The floating point registers on the Intel processor are 80 bits, and the floating point operations on the processor are performed using this precision. On this platform, JIT can generate floating point instructions that load arguments into their own 80-bit registers, and the only difference is that the registers are loaded from 32-bit or 64-bit memory locations. Perhaps the Math.Min developers based their implementation on confusing knowledge of how the JIT compiler generates floating point code.

From the accepted answer it is clear that the question is based on the wrong assumption (which is different from Single to Double ). To examine whether an actor really makes a difference, I looked at the assembler generated by the Min function with and without cast.

This is the .NET framework Math.Min(Single, Single) , as shown in my debugger:

  00000000 push ebp 
 00000001 mov ebp, esp 
 00000003 fld dword ptr [ebp + 0Ch] 
 00000006 fld dword ptr [ebp + 8] 
 00000009 fxch st (1) 
 0000000b fcomi st, st (1) 
 0000000d jp 00000015 
 0000000f jae 00000015 
 00000011 fstp st (1) 
 00000013 jmp 00000022 
 00000015 fcomi st, st (0) 
 00000017 jp 0000001B 
 00000019 je 00000026 
 0000001b mov eax, 1 
 00000020 jmp 00000028 
 00000022 pop ebp 
 00000023 ret 8 
 00000026 xor eax, eax 
 00000028 test eax, eax 
 0000002a je 00000030 
 0000002c fstp st (1) 
 0000002e jmp 00000036 
 00000030 fstp st (0) 
 00000032 pop ebp 
 00000033 ret 8 
 00000036 pop ebp 
 00000037 ret 8 

Here is the assembly for the function using the cast to Double exactly the same as in the question:

  00000000 push ebp 
 00000001 mov ebp, esp 
 00000003 sub esp, 8 
 00000006 fld dword ptr [ebp + 0Ch] 
 00000009 fld dword ptr [ebp + 8] 
 0000000c fld st (1) 
 0000000e fstp qword ptr [ebp-8] 
 00000011 fld qword ptr [ebp-8] 
 00000014 fld st (1) 
 00000016 fstp qword ptr [ebp-8] 
 00000019 fld qword ptr [ebp-8] 
 0000001c fcomip st, st (1) 
 0000001e fstp st (0) 
 00000020 jp 00000028 
 00000022 jbe 00000028 
 00000024 fstp st (0) 
 00000026 jmp 00000043 
 00000028 fxch st (1) 
 0000002a fcomi st, st (0) 
 0000002c jp 00000030 
 0000002e je 00000037 
 00000030 mov eax, 1 
 00000035 jmp 00000039 
 00000037 xor eax, eax 
 00000039 test eax, eax 
 0000003b jne 00000041 
 0000003d fstp st (0) 
 0000003f jmp 00000049 
 00000041 fstp st (1) 
 00000043 mov esp, ebp 
 00000045 pop ebp 
 00000046 ret 8 
 00000049 mov esp, ebp 
 0000004b pop ebp 
 0000004c ret 8 

There are a few more instructions, and this is likely to slightly decrease the performance of the function.

+1
source

Source: https://habr.com/ru/post/1403611/


All Articles