Pull to refresh

Comments 19

Проверка на равенство double переменных в C#? Серьёзно?

В каком месте вы увидели проверку на равенство double переменных?

if (a1[i1] < a2[i2]) {
//...
} else if (a2[i2] < a1[i1]) {
//...
} else {
// при каком условии мы сюда попадём?
}
  public static void Main()
  {
    double a = 1d;
    double b = 0.9999999999999999989d;
    Console.WriteLine(a == b);
    // Outputs: True
  }

В .NET, начиная с первой версии, существует Double.Epsilon, которое используется при сравнении чисел. Но, нет гарантии, что от платформы к платформе он не будет меняться.
Да и в целом, из-за особенностей представления в памяти чисел с плавающей точкой, всегда рекомендуется производить их сравнение с явным определением точности. Это ещё на информатике в школе рассказывали.
Это ответ на вопрос:
// при каком условии мы сюда попадём?
Когда разница будет настолько мала, что операции над числами с плавающей точкой её не заметят.

А какие есть другие варианты в этом случае? Обычное сравнение с эпсилоном ломает корректность, т.к. транзитивность.
К тому же, этот алгоритм не приводит к новым ошибкам или округлениям.

А что такого? Например, вполне могут быть проверки что double/float делитель не равен ровно нулю, чтобы не получить 0/0 = NaN. Или функция signum, которая очень даже отдельно рассматривает нуль.

А с каким нулём вы обычно сравниваете с +0 или -0?
Какая разница?

+0.0 == -0.0 // true

Also, при чём здесь моя личность? В компьютерах, 0.0/0.0 == NaN потому что так написано в IEEE 754
думаю, что когда проходит jit, то эти конверсии инлайнятся, и при генерации нативного кода в стандартных местах используются bit-twiddling хаки. Ну и помогает этому то, что размер boolean такой же, как и у int32 и эта конверсия в итоге превратится на x86 машине во что-нибудь типа
cmp eax, 0
sizeof(bool) возвращает 1, может меняется в зависимости от платформы но все же
Несколько удивительно в таком формате от Александреску. Обычно он предлагает смотреть машинный код (что правильно), видимо тут просто лайт-версия, чтобы не травмировать нежную психику. А то ведь можно упомянуть, что у многих современных архитектур есть conditional-move (на x86, скорее всего, jit заменит ветвления на CMOVxx). Так же известно, что CMOVxx немного медленнее правильно предсказанного ветвления Jxx (по вполне рациональным причинам). И отсюда цифры в -18% и +200% становятся вполне очевидными (и даже предсказуемыми).
странно, что медленнее, ведь в железе это выглядит всего лишь как мультиплексор, а затраты на декодирование JXX и CMOVXX сопоставимы
Не могу вспомнить источник информации, но насколько я помню, при правильном предсказании ветвления команда Jxx удаляется с конвеера полностью (предсказатель ветвлений работает независимо от конвеера), а CMOVxx выполняется всегда (выполняется цикл записи), условие только выбирает куда записать результат: в точку назначения или в «пустоту».

Почему так сделано — не знаю, но наверняка для этого есть причины. Например, что просто удаляя правильно предсказанную команду Jxx нам не надо учитывать много факторов, мы просто подгружаем в конвеер данные с предполагаемого адреса, а в случае факапа в точке предполагаемого ветвления перезапускаем конвеер (привет Meltdown и Spectre).
А удаление CMOVxx завязано на переименование регистров, отложенные вычисления и Зевс знает какие ещё оптимизации. Видимо оно того не стоит.
А почему нельзя просто использовать switch/case, который разворачивается в branch table? Почему бы не применить loop unrolling? Это же элементарные вещи, которые не зависят от языка/компилятора.
перенести минимумы каждого квартиля массива в первый квартиль. Другими словами, массив нужно поделить на четыре части и минимальное значение каждой части поместить в начало массива.

Квартиль означает что-то другое, здесь лучше использовать слово четверть.
Sign up to leave a comment.