Как применить побитовые операции к фактическому представлению чисел JS в формате IEEE 754?

#javascript #bit-manipulation #bitwise-operators #ieee-754

#javascript #манипулирование битами #побитовые операторы #ieee-754

Вопрос:

В JavaScript всякий раз, когда вы выполняете побитовую операцию, такую как x << 2 , 64-разрядное представление float преобразуется в 32-разрядное unsigned int, прежде чем фактически произойдет сдвиг. Я заинтересован в применении сдвига к фактическому, неизмененному побитовому представлению IEEE 754.

Как это возможно?

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

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

2. Предполагаю, что это не должно быть общей частью этого языка для чисел IEEE 754, состоящих из разных полей, упакованных в 64 бита. При сдвиге битов неясно, применяется ли сдвиг к отдельному полю или ко всем «необработанным» 64 битам без учета поля мантиссы, знака или экспоненты. В последнем случае речь фактически идет о преобразовании JSNumber в 64-битное целое число, чтобы впоследствии сдвинуть последнее.

3. И каким был бы результат << 2, если бы вы могли получить это представление? Сдвиг битов вслепую при игнорировании отдельных полей significant / exponent не был бы полезен в числовом контексте. Умножение на 4 было бы правильным, но тогда вам не нужны биты. Это оставляет только нечисловые контексты, но тогда зачем вам значение с плавающей точкой?

4. Точка на всякий случай: я получаю эти значения с плавающей точкой из программы на языке Си, используя node-ffi . Эта программа на C использует младший значащий бит значения float, чтобы пометить его специальным флагом. Мне нужен способ проверить этот флаг в JavaScript.

Ответ №1:

Вы могли бы попробовать сначала преобразовать JSNumber в байты / целые числа и изменить результат самостоятельно.

Использование материалов TypedArray, доступных в последних версиях основных браузеров:

 var f = new Float64Array( 1 );    // creating typed array to contain single 64-bit IEEE754
f.set( [ 1.0 ], 0 );              // transferring JSNumber for untyped array to first element of typed one
var d = new DataView( f.buffer ); // creating raw view on content in typed array
var w1 = d.getUint32( 0 );        // accessing bytes 0 to 3 of typed array
var w2 = d.getUint32( 4 );        // accessing bytes 4 to 7 of typed array
  

После этого вы могли бы сдвинуть 32-битные слова в w1 и w2 самостоятельно перенести 2 старших бита в нижнем слове на 2 младших бита верхнего слова.

Порядковость может контролироваться при использовании второго аргумента для d.getUint32() .

Читать далее: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays

Просто для того, чтобы убедиться, что комментарий Берги распознан должным образом. Весь мой код мог бы быть сведен к одной строке вот так:

 var d = new Uint32Array( new Float64Array( [1.0] ).buffer );
  

d[0] и d[1] подходят для доступа к содержащимся 32-битным словам, тогда.

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

1. Проще: var d = new Uint32Array( new Float64Array( [1.0] ).buffer) 🙂

2. Действительно … Я не знаком с этим «новым» материалом, на самом деле я сам впервые читал API. Мой код был получен из связанных документов и протестирован только в сеансе консоли. Хотя это довольно классный API …

3. Привет, спасибо. Это действительно ответ на мой вопрос. Однако, учитывая представленную здесь информацию, я немного смягчил спецификации. Теперь все, что мне нужно, это определить, установлен ли младший значащий бит значения float или нет. Я хотел бы знать, есть ли какой-либо более простой (быстрый) способ сделать это, или ваше решение является лучшим вариантом. Если вы знаете ответ, пожалуйста, проверьте мой последний вопрос!

4. Без тестирования я склонен рекомендовать данный подход по сравнению с любым другим, в соответствии с вашим намерением протестировать LSB мантиссы. Типизированные массивы вводятся для обеспечения низкоуровневого доступа к потокам данных, низкоуровневый обычно подразумевает более высокую производительность. Единственная альтернатива, которую я вижу, заключается в следующем: создайте число с очищенным LSB мантиссы, но сопоставляя все остальные биты знака, экспоненты и мантиссы … затем сравните их. Однако построение этой маленькой мантиссы (это 2 ^ -52) совсем не так просто, как приведенная выше строка … требуется обработка строк и / или последовательностей арифметики с плавающей запятой.

5. Хорошо, похоже, что это решение подходит идеально. Однако я использую существующий DataView, поэтому мне не нужно воссоздавать массивы при каждом вызове (как в последнем примере)! Обеспечена более высокая производительность в тестах. Спасибо.