迟到了,但我想用一种更快、更简单和更好支持的完整 53 位方法 来赞美 trincot 的 32 位答案。
以下 2 个示例将仅读取/解析并返回浮点数的指数值。
对于支持 ES6 的现代浏览器ArrayBuffer
(DataView
不关心平台的字节序,但遗留兼容性较差):
reqBits4Int = (function(d){ 'use strict';
return function(n){
return n && ( // return 0 if n===0 (instead of 1)
d.setFloat64(0, n), // OR set float to buffer and
d.getUint16(0) - 16352 >> 4 & 2047 // return float's parsed exponent
); // Offset 1022<<4=16352; 0b1111111=2047
}; // DataView methods default to big-endian
})( new DataView(new ArrayBuffer(8)) ); // Pass a Buffer's DataView to IFFE
Float64Array
支持and的稍旧浏览器的示例Uint16Array
(但不支持DataView
,因此字节序取决于平台,此代码段假定为“标准”小字节序):
reqBits4Int = (function(){ 'use strict';
var f = new Float64Array(1), // one 8-byte element (64bits)
w = new Uint16Array(f.buffer, 6); // one 2-byte element (start at byte 6)
return function(n){
return ( f[0] = n // update float array buffer element
// and return 0 if n===0 (instead of 1)
) && w[0] - 16352 >> 4 & 2047; // or return float's parsed exponent
}; //NOTE : assumes little-endian platform
})(); //end IIFE
以上两个版本都返回一个正整数Number
,表示保存作为参数传递的整数所需的最大位数。
它们在 [-2 53 , 2 53 ]的整个范围内无错误地工作,除此之外,覆盖正浮点指数的整个浮点范围,除非输入已经发生舍入(例如 2 55 -1)被存储为 2 55(显然,它等于 56 位)。Number
Number
解释 IEEE 754 浮点格式确实超出了这个答案的范围,但对于那些有基本理解的人,我在下面包含了折叠的片段,其中包含表格形式的计算,从中可以看到/解释逻辑:实际上我们只需抓住浮点数的第一个字(16 个 MSB,包含符号和全指数),减去 4 位移位偏移和 zeroing_offset(节省我们 2 次操作),移位和掩码结果作为输出。0
在功能中得到照顾。
<xmp> PREVIEW of data to be generated:
Float value : S_exponent__MMMM : # -(1022<<4)#### : # >> 4 : & 2047 : Result integer
-9 : 1100000000100010 : 1000000001000010 : 100000000100 : 100 : 4
-8 : 1100000000100000 : 1000000001000000 : 100000000100 : 100 : 4
-7 : 1100000000011100 : 1000000000111100 : 100000000011 : 11 : 3
-6 : 1100000000011000 : 1000000000111000 : 100000000011 : 11 : 3
-5 : 1100000000010100 : 1000000000110100 : 100000000011 : 11 : 3
-4 : 1100000000010000 : 1000000000110000 : 100000000011 : 11 : 3
-3 : 1100000000001000 : 1000000000101000 : 100000000010 : 10 : 2
-2 : 1100000000000000 : 1000000000100000 : 100000000010 : 10 : 2
-1 : 1011111111110000 : 1000000000010000 : 100000000001 : 1 : 1
0 : 0 : -11111111100000 : -1111111110 : 10000000010 : 1026
1 : 11111111110000 : 10000 : 1 : 1 : 1
2 : 100000000000000 : 100000 : 10 : 10 : 2
3 : 100000000001000 : 101000 : 10 : 10 : 2
4 : 100000000010000 : 110000 : 11 : 11 : 3
5 : 100000000010100 : 110100 : 11 : 11 : 3
6 : 100000000011000 : 111000 : 11 : 11 : 3
7 : 100000000011100 : 111100 : 11 : 11 : 3
8 : 100000000100000 : 1000000 : 100 : 100 : 4
9 : 100000000100010 : 1000010 : 100 : 100 : 4
after 18 the generated list will only show 3 values before and after the exponent change
</xmp>
<script> //requires dataview, if not available see post how to rewrite or just examine example above
firstFloatWord = (function(d){
return function(n){
return d.setFloat64(0, n), d.getUint16(0);
};
})( new DataView(new ArrayBuffer(8)) );
function pad(v, p){
return (' '+v).slice(-p);
}
for( var r= '', i=-18, c=0, t
; i < 18014398509481984
; i= i>17 && c>=5
? (r+='\n', c=0, (i-2)*2-3)
: (++c, i+1)
){
r+= pad(i, 19) + ' : '
+ pad((t=firstFloatWord(i)).toString(2), 17) + ' : '
+ pad((t-=16352).toString(2), 17) + ' : '
+ pad((t>>=4).toString(2), 13) + ' : '
+ pad((t&=2047).toString(2), 12) + ' : '
+ pad(t, 5) + '\n';
}
document.body.innerHTML='<xmp> Float value : S_exponent__MMMM : # -(1022<<4)#### : '
+ ' # >> 4 : & 2047 : Result integer\n' + r + '</xmp>';
</script>
后备选项:
ECMAScript (javascript) 让实现者可以自由选择如何实现该语言。因此,在狂野的 x 浏览器世界中,不仅要处理舍入差异,还要处理不同的算法,例如Math.log
等等Math.log2
。
正如您已经注意到的(您的方法 1),一个log2
(polyfill) 可能不起作用的常见示例是2 48(= 49,当地板时是一比一),但这不是唯一的例子。
例如,某些版本的 chrome 甚至会搞砸小得多的数字,例如:(Math.log2(8) = 2.9999999999999996
落地时减少一到二)。
在此 stackoverflow Q/A 中阅读更多相关信息:Chrome 中的 Math.log2 精度已更改。
这意味着我们无法知道何时对数结果下限或上限(或在四舍五入 之前轻松预测我们何时已经过关)。
因此,您可以计算输入数字在循环中小于 1 之前除以 2 的频率(很像您计算的 32 位移位方法 2):
function reqBits4Int(n){ for(var c=0; n>=1; ++c, n/=2); return c }
但这是相当蛮力的(也可能让你陷入四舍五入的问题)。您可以使用一些分而治之的方法来改进这一点,当您使用它时,展开循环:
function reqBits4Int(n){ 'use strict';
var r= 4294967295 < n ? ( n= n/4294967296 >>> 0, 32 ) : 0 ;
65535 < n && ( n >>>= 16, r+= 16 );
255 < n && ( n >>= 8, r+= 8 );
15 < n && ( n >>= 4, r+= 4 );
// 3 < n && ( n >>= 2, r+= 2 );
// 1 < n && ( n >>= 1, r+= 1 );
// return r + n;
// OR using a lookup number instead of the lines comented out above
// position: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 = 16 values
// binary: 11 11 11 11 11 11 11 11 10 10 10 10 01 01 00 00 = 4294945360 dec
return (n && (4294945360 >>> n * 2 & 3) + 1) + r;
}
格式化旨在帮助理解算法。它会缩小得非常好!
这具有 [0, 2 53 ]的正整数范围,没有错误(最多 2 64具有相同的可预测舍入-“错误”)。
或者,您可以尝试其他一些(重复,对于大于 32 位的输入值)的 bithacks。
最简单和最短的(但可能更慢,与上面的计算片段相比)是对数字进行字符串化并计算结果字符串长度,如 Anurag 的答案,本质上是:(return n && n.toString(2).length;
假设浏览器可以给出高达(至少)53 的结果位)。