Реализация .NET: двойное преобразование в Math.Min (float, float) - PullRequest
4 голосов
/ 27 марта 2012

Почему .Net реализует функцию Math.Min (float, float) следующим образом:

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

Хотя я вижу использование IsNaN, я не понимаю, почему они сравниваются в удвоение при сравненииценности.Разве это не медленнее, чем просто писать val < val 2?Особенно, если я хочу использовать его, чтобы привязать значение к числам без особой точности, например 0f или 1f.

Должен ли я просто реализовать собственную математическую библиотеку, которая дополнительно требует наличия не NaNзначения, чтобы получить лучшую производительность или это пустая трата времени?

public static float Min(float a, float b)
{
    return a < b ? a : b;
}

Ответы [ 4 ]

5 голосов
/ 27 марта 2012

Да, я использовал Resharper, который делает именно это

И в этом проблема, он шарит в коде.Исходный код доступен из Reference Source , он выглядит следующим образом:

  [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;
  }

Трудно догадаться, почему Resharper делает это, я бы с готовностью предположил, что это просто ошибка.Подарите исходный код, доступный в Microsoft, не только для точности, но и для комментариев.

2 голосов
/ 27 марта 2012

Я не могу сказать вам, почему они выбрали приведение float к double для сравнения, но я сомневаюсь, что это окажет какое-либо заметное или значительное влияние на скорость вашей программы.Я бы просто использовал встроенную функцию.

1 голос
/ 27 марта 2012

Как правило, вы не можете предположить, что Single работает быстрее, чем Double.Регистры с плавающей запятой на процессоре Intel являются 80-битными, а операции с плавающей запятой на процессоре выполняются с такой точностью.На этой платформе JIT может генерировать инструкции с плавающей запятой, которые загружают аргументы в собственные 80-битные регистры, и единственное отличие состоит в том, что регистры загружаются из 32-битных или 64-битных ячеек памяти.Возможно, разработчики Math.Min основали свою реализацию на сложном знании того, как JIT-компилятор генерирует код с плавающей запятой.

Из принятого ответа видно, что вопрос основан на неверном предположении (что существуетбросок от Single до Double).Чтобы выяснить, действительно ли приведение имеет значение, я посмотрел на ассемблер, сгенерированный функцией Min с приведением и без преобразования.

Это .NET Framework Math.Min(Single, Single), как видно из моего отладчика:

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 

Вот сборка для функции, использующей приведение к Double, как в вопросе:

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 

Есть еще несколько инструкций, которые, вероятно, снизят производительностьфункции немного.

1 голос
/ 27 марта 2012

Нет такой вещи как Math.Min (плавать, плавать). .NET содержит только определение для Min (double, double). Проверьте MSDN: http://msdn.microsoft.com/en-us/library/system.math.min(v=vs.100).aspx

РЕДАКТИРОВАТЬ: есть, спасибо @gideon http://msdn.microsoft.com/en-us/library/070xee48.aspx

...