Что самый легкий путь состоит в том, чтобы преобразовать число с плавающей точкой в свое двоичное представление в JavaScript? (например, 1.0-> 0x3F800000).
Я попытался сделать это вручную, и это работает в некоторой степени (с обычными числами), но это перестало работать для очень больших или очень небольших чисел (никакая проверка диапазона) и для особых случаев (NaN, бесконечность, и т.д.):
function floatToNumber(flt)
{
var sign = (flt < 0) ? 1 : 0;
flt = Math.abs(flt);
var exponent = Math.floor(Math.log(flt) / Math.LN2);
var mantissa = flt / Math.pow(2, exponent);
return (sign << 31) | ((exponent + 127) << 23) | ((mantissa * Math.pow(2, 23)) & 0x7FFFFF);
}
Я изобретаю велосипед?
Править: Я улучшил свою версию, теперь она обрабатывает особые случаи.
function assembleFloat(sign, exponent, mantissa)
{
return (sign << 31) | (exponent << 23) | (mantissa);
}
function floatToNumber(flt)
{
if (isNaN(flt)) // Special case: NaN
return assembleFloat(0, 0xFF, 0x1337); // Mantissa is nonzero for NaN
var sign = (flt < 0) ? 1 : 0;
flt = Math.abs(flt);
if (flt == 0.0) // Special case: +-0
return assembleFloat(sign, 0, 0);
var exponent = Math.floor(Math.log(flt) / Math.LN2);
if (exponent > 127 || exponent < -126) // Special case: +-Infinity (and huge numbers)
return assembleFloat(sign, 0xFF, 0); // Mantissa is zero for +-Infinity
var mantissa = flt / Math.pow(2, exponent);
return assembleFloat(sign, exponent + 127, (mantissa * Math.pow(2, 23)) & 0x7FFFFF);
}
Я все еще не уверен, работает ли это 100% правильно, но это, кажется, работает достаточно хорошее. (Я все еще ищу существующие реализации).
Вот функция, которая работает со всем, на чем я ее тестировал, за исключением того, что она не различает -0.0 и +0.0.
Он основан на коде из http://jsfromhell.com/classes/binary-parser , но специализируется на 32-битных числах с плавающей запятой и возвращает целое число вместо строки. Я также изменил его, чтобы сделать его быстрее и (немного) более читаемым.
// Based on code from Jonas Raoni Soares Silva
// http://jsfromhell.com/classes/binary-parser
function encodeFloat(number) {
var n = +number,
status = (n !== n) || n == -Infinity || n == +Infinity ? n : 0,
exp = 0,
len = 281, // 2 * 127 + 1 + 23 + 3,
bin = new Array(len),
signal = (n = status !== 0 ? 0 : n) < 0,
n = Math.abs(n),
intPart = Math.floor(n),
floatPart = n - intPart,
i, lastBit, rounded, j, exponent;
if (status !== 0) {
if (n !== n) {
return 0x7fc00000;
}
if (n === Infinity) {
return 0x7f800000;
}
if (n === -Infinity) {
return 0xff800000
}
}
i = len;
while (i) {
bin[--i] = 0;
}
i = 129;
while (intPart && i) {
bin[--i] = intPart % 2;
intPart = Math.floor(intPart / 2);
}
i = 128;
while (floatPart > 0 && i) {
(bin[++i] = ((floatPart *= 2) >= 1) - 0) && --floatPart;
}
i = -1;
while (++i < len && !bin[i]);
if (bin[(lastBit = 22 + (i = (exp = 128 - i) >= -126 && exp <= 127 ? i + 1 : 128 - (exp = -127))) + 1]) {
if (!(rounded = bin[lastBit])) {
j = lastBit + 2;
while (!rounded && j < len) {
rounded = bin[j++];
}
}
j = lastBit + 1;
while (rounded && --j >= 0) {
(bin[j] = !bin[j] - 0) && (rounded = 0);
}
}
i = i - 2 < 0 ? -1 : i - 3;
while(++i < len && !bin[i]);
(exp = 128 - i) >= -126 && exp <= 127 ? ++i : exp < -126 && (i = 255, exp = -127);
(intPart || status !== 0) && (exp = 128, i = 129, status == -Infinity ? signal = 1 : (status !== status) && (bin[i] = 1));
n = Math.abs(exp + 127);
exponent = 0;
j = 0;
while (j < 8) {
exponent += (n % 2) << j;
n >>= 1;
j++;
}
var mantissa = 0;
n = i + 23;
for (; i < n; i++) {
mantissa = (mantissa << 1) + bin[i];
}
return ((signal ? 0x80000000 : 0) + (exponent << 23) + mantissa) | 0;
}