C / C эквивалент doubleToRawLongBits Java()

#java #c #c

#java #c #c

Вопрос:

В Java Double.doubleToLongBits() это полезно для реализации hashCode() методов.

Я пытаюсь сделать то же самое на C и написать свой собственный doubleToRawLongBits() метод, так как после поиска в Google я не могу найти подходящую реализацию.

Я могу получить значение и показатель степени из std::frexp(numbr,amp;exp) и могу определить знак, но не могу понять использование побитовых операторов для получения эквивалента Java.

Например, Java Double.doubleToLongBits() возвращает следующее для double 3.94:

4616054510065937285

Спасибо за любую помощь.

Грэм

Ниже приведена документация, скопированная и вставленная из Double.Двойные или длинные биты()

 ===Java Double.doubleToRawLongBits() description===

/**
     * Returns a representation of the specified floating-point value
     * according to the IEEE 754 floating-point "double
     * format" bit layout, preserving Not-a-Number (NaN) values.
     * <p>
     * Bit 63 (the bit that is selected by the mask 
     * <code>0x8000000000000000L</code>) represents the sign of the 
     * floating-point number. Bits 
     * 62-52 (the bits that are selected by the mask 
     * <code>0x7ff0000000000000L</code>) represent the exponent. Bits 51-0 
     * (the bits that are selected by the mask 
     * <code>0x000fffffffffffffL</code>) represent the significand 
     * (sometimes called the mantissa) of the floating-point number. 
     * <p>
     * If the argument is positive infinity, the result is
     * <code>0x7ff0000000000000L</code>.
     * <p>
     * If the argument is negative infinity, the result is
     * <code>0xfff0000000000000L</code>.
     * <p>
     * If the argument is NaN, the result is the <code>long</code>
     * integer representing the actual NaN value.  Unlike the
     * <code>doubleToLongBits</code> method,
     * <code>doubleToRawLongBits</code> does not collapse all the bit
     * patterns encoding a NaN to a single amp;quot;canonicalamp;quot; NaN
     * value.
     * <p>
     * In all cases, the result is a <code>long</code> integer that,
     * when given to the {@link #longBitsToDouble(long)} method, will
     * produce a floating-point value the same as the argument to
     * <code>doubleToRawLongBits</code>.
     *
     * @param   value   a <code>double</code> precision floating-point number.
     * @return the bits that represent the floating-point number.
     * @since 1.3
     */
    public static native long doubleToRawLongBits(double value);
 

Комментарии:

1. основная проблема с / C заключается в том, что двойная двоичная форма не определена.

Ответ №1:

Достаточно простого приведения:

 double d = 0.5;

const unsigned char * buf = reinterpret_cast<const unsigned char *>(amp;d);

for (unsigned int i = 0; i != sizeof(double);   i)
  std::printf("The byte at position %u is 0xX.n", i, buf[i]);
 

Где находятся знаковый бит и биты экспоненты, зависит от вашей платформы и порядкового номера. Если ваши значения с плавающей запятой равны IEE754, если знак и показатель находятся впереди и если CHAR_BIT == 8 , вы можете попробовать это:

 const bool sign = buf[0] amp; 0x80;
const int exponent = ((buf[0] amp; 0x7F) << 4)   (buf[1] >> 4) - 1023;
 

(В C, скажем (const unsigned char *)(amp;d) , для приведения.)

Обновление: чтобы создать целое число с теми же битами, вы должны сначала создать целое число, а затем скопировать:

 unsigned long long int u;
unsigned char * pu = reinterpret_cast<unsigned char *>(amp;u);
std::copy(buf, buf   sizeof(double), pu);
 

Для этого вы должны иметь в виду несколько вещей: размер целого числа должен быть достаточным (статическое утверждение для sizeof(double) <= sizeof(unsigned long long int) должно сделать трюк), и если целое число на самом деле больше, то вы копируете только его части. Я уверен, что вы поймете это, хотя 🙂 (Вы могли бы использовать некоторую магию шаблона для создания целого числа правильного размера, если вы действительно хотите.)

Комментарии:

1. И генерировать long из const unsigned char * buf?

Ответ №2:

 #include <stdint.h>

static inline uint64_t doubleToRawBits(double x) {
    uint64_t bits;
    memcpy(amp;bits, amp;x, sizeof bits);
    return bits;
}
 

Комментарии:

1. Это предполагает, что размер double равен 64 битам, что не обязательно так.

2. @RobK: конечно. Но на платформах, где double не сопоставляется с IEEE -754 double, битовое представление вряд ли будет очень полезным для программы, которая ожидает этого.

3. Судя по упоминанию функции hashcode() в исходном вопросе, похоже, что ему просто нужна уникальная строка битов, представляющая double для использования в хэш-функции или что-то в этом роде.

4. Да, например, hashCode() для объекта Point3D может быть написан на Java; который на самом деле легко автоматически генерируется с помощью NetBeans:public int hashCode() { int hash = 3; hash = 59 * hash (int) (Double . doubleToLongBits(this.mx ) ^ (Двойной. doubleToLongBits(this.mx ) >>> 32)); хэш = 59 * хэш (int) (Двойной. doubleToLongBits(this.my ) ^ (Двойной. doubleToLongBits(this.my ) >>> 32)); хэш = 59 * хэш (int) (Двойной. doubleToLongBits(this.mz ) ^ (Двойной. doubleToLongBits(this.mz ) >>> 32)); возвращает хэш; }

5. Приведенный выше doubleToRawBits() с использованием memcpy() дает точно такой же результат, что и Java для примера 3.94. Это тоже самое простое решение.

Ответ №3:

Мне нравятся объединения для таких вещей.

 union double_and_buffer {
    double d;
    unsigned char byte_buff[ sizeof(double) ];
} dab;

dab.d = 1.0;
for ( int i = 0; i < sizeof(dab.byte_buff);   i )
{
    cout << hex byte_buff[ i ];
}
 

Я думаю, это делает более понятным, что вы делаете, и позволяет компилятору выполнять всю математику.

Комментарии:

1. Я часто использую этот подход, но имейте в виду, что многие современные компиляторы нарушат его, если анализ псевдонимов на основе типов явно не отключен.

2. @StephenCanon Я знаю, что теоретически чтение члена union, который не был установлен, является неопределенным поведением, но действительно ли существует компилятор, который создает проблемы для такой хорошо известной и часто используемой идиомы?

3. @Voo: За эти годы их было несколько; в первую очередь серия сборок GCC где-то около 4.1.

4. Это неопределенное поведение. Другие решения на этой странице являются правильными.

5. Microsoft использует такого рода конструкции повсеместно в своем API, например, в USB_HUB_CAP_FLAGS . Стандарт требует, чтобы «размер объединения был достаточным для того, чтобы содержать самый большой из его нестатических элементов данных. Каждый нестатический элемент данных выделяется так, как если бы он был единственным элементом структуры. Все нестатические элементы данных объекта объединения имеют один и тот же адрес » ( open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf , раздел 9.5). Следовательно, использование объединения таким образом фактически должно быть эквивалентно reinterpret_cast<>() по адресу желаемой переменной