Как проверить, подходит ли long long в двойную переменную

Я хотел бы проверить, можно ли безопасно преобразовать длинную переменную в двойную. DBL_MAX не помогает, потому что есть целые числа меньшие, чем то, которые не могут быть представлены двойным числом, в то время как некоторые из целых чисел, превышающих 2^53, все еще могут уместиться.

Есть ли надежный способ сделать это? Может ли компилятор оптимизировать оператор, подобный приведенному ниже?

(long long)((double)a) == a (где a - это long long)

Это не требует наибольшего целого числа, которое может быть представлено как двойное, я прошу общую функцию, которая может проверить, могу ли я точно преобразовать любое long long значение в удвоение без ошибок .


person wesolyromek    schedule 18.04.2016    source источник
comment
Не могли бы вы определить, что безопасно превратить в двойное средство?   -  person jdarthenay    schedule 18.04.2016
comment
Проверить, равны ли последние биты a нулю, если a больше, чем pow (2, 53)? clz(abs(a)) + ctz(abs(a)) > 64 - 53?   -  person EOF    schedule 18.04.2016
comment
Я думаю, что автор спрашивает: как я могу гарантировать отсутствие ошибки округления, когда я сохраняю целое число long long в double, учитывая, что есть целые значения, которые двойное число не может сохранить?   -  person TezlaCoil    schedule 18.04.2016
comment
В наиболее распространенных реализациях с плавающей запятой IEEE754 это невозможно.   -  person too honest for this site    schedule 18.04.2016
comment
@ user3470630 Вот о чем я думал, пока не увидел последнюю строчку. (long long) (3.12) == 3 следует рассматривать как истину.   -  person jdarthenay    schedule 18.04.2016
comment
@jdarthenay В последней строке я предположил, что a равно long long   -  person wesolyromek    schedule 18.04.2016
comment
Не в этом суть. Я имею в виду, что если (double) 3 3.12, ваш последний тест сказал бы, что 3.12 является безопасным приведением для 3 как двойного ...   -  person jdarthenay    schedule 18.04.2016
comment
Я думаю, что ваше выражение в порядке, за исключением большого целого числа, где (double) a равно 2 ^ 63, и вам нужно проверить, может ли преобразование обратно в long long быть неопределенным поведением.   -  person gnasher729    schedule 18.04.2016
comment
Проголосовал за повторное открытие этого сообщения и проголосовал за его дублирование, хотя и в отношении long long, и double задаются принципиально разные вопросы.   -  person chux - Reinstate Monica    schedule 19.04.2016


Ответы (2)


Метод OP - хорошее начало.

(long long)((double)a) == a

Пока есть проблема. Например. long long a = LLONG_MAX; ((double)a) результаты - округленное значение, превышающее LLONG_MAX.

Следующее, конечно, не будет переполняться double.
(Патологическое исключение: LLONG_MIN превышает -DBL_MAX).

volatile double b = (double) a;

Для достижения цели OP достаточно возврата к long long и тестирования на a. Необходимо только убедиться, что b находится в long long диапазоне. @ gnasher729 Предположим, что дополнение до 2 и double использует FLT_RADIX != 10. В этом случае самый низкий long long - это степень двойки, а самый высокий - степень двойки минус 1, и преобразование в double может быть выполнено точным путем тщательного расчета long long пределов следующим образом.

bool check_ll(long long a) {
  constant double d_longLong_min = LLONG_MIN;
  constant double d_longLong_max_plus_1 = (LLONG_MAX/2 + 1)*2.0;
  volatile double b = (double) a;
  if (b < d_longLong_min || b >= d_longLong_max_plus_1) {
    return false;
  }
  return (long long) b == a;
}

[редактировать упрощать - более общие]

Тест b рядом с LLONG_MIN необходим только тогда, когда long long не использует дополнение 2

bool check_ll2(long long a) {
  volatile double b = (double) a;
  constant double d_longLong_max_plus_1 = (LLONG_MAX/2 + 1)*2.0;
  #if LLONG_MIN == -LLONG_MAX
    constant double d_longLong_min_minus_1 = (LLONG_MIN/2 - 1)*2.0;;
    if (b <= d_longLong_min_minus_1 || b >= d_longLong_max_plus_1) {
      return false;
    }
  #else
    if (b >= d_longLong_max_plus_1) {
      return false;
    }
  #endif
  return (long long) b == a;
}

Я не ожидал, что компиляция сможет оптимизировать (long long)((double)a) == a. IAC, используя промежуточный код volatile double, предотвращает это.

person chux - Reinstate Monica    schedule 18.04.2016

Я не уверен, что вы можете проверить это преобразование перед приведением, но fenv.h кажется, что оно может помочь вам при проверке после преобразования. FE_INEXACT может позволить вам проверить, нельзя ли точно сохранить только что выполненную операцию. http://www.cplusplus.com/reference/cfenv/FE_INEXACT/

person TezlaCoil    schedule 18.04.2016