val*_*man 16 javascript math haskell ghc ieee-754
是否有一种相当快速的方法从Javascript中的数字中提取指数和尾数?
AFAIK有没有办法在后面的Javascript一个号码,这使得它在我看来位拿到我正在寻找一个因子分解问题:找到m并n使得2^n * m = k对于给定的k.由于整数分解是在NP中,我只能假设这将是一个相当难的问题.
我正在实现一个用于生成Javascript的GHC插件,需要实现decodeFloat_Int#和decodeDouble_2Int# 原始操作 ; 我想我可以重写使用该操作的基本库的部分来做他们正在以其他方式进行的操作(这不应该太难,因为所有数字类型都有数字作为他们的表示,但它'如果我不需要,那就好了.
有没有办法以一种甚至是高效的方式,通过一些黑暗的Javascript伏都教,聪明的数学或其他方式来做到这一点,或者我应该只是扣下来并拥有基础库?
编辑 根据ruakh和Louis Wasserman的出色答案,我提出了以下实现,这似乎运作得很好:
function getNumberParts(x) {
if(isNaN(x)) {
return {mantissa: -6755399441055744, exponent: 972};
}
var sig = x > 0 ? 1 : -1;
if(!isFinite(x)) {
return {mantissa: sig * 4503599627370496, exponent: 972};
}
x = Math.abs(x);
var exp = Math.floor(Math.log(x)*Math.LOG2E)-52;
var man = x/Math.pow(2, exp);
return {mantissa: sig*man, exponent: exp};
}
Run Code Online (Sandbox Code Playgroud)
cop*_*opy 21
使用新的ArrayBuffer访问数组,实际上可以通过从中提取它们来检索精确的尾数和指数Uint8Array.如果您需要更快的速度,请考虑重复使用Float64Array.
function getNumberParts(x)
{
var float = new Float64Array(1),
bytes = new Uint8Array(float.buffer);
float[0] = x;
var sign = bytes[7] >> 7,
exponent = ((bytes[7] & 0x7f) << 4 | bytes[6] >> 4) - 0x3ff;
bytes[7] = 0x3f;
bytes[6] |= 0xf0;
return {
sign: sign,
exponent: exponent,
mantissa: float[0],
}
}
Run Code Online (Sandbox Code Playgroud)
我还创建了一些测试用例.0失败,因为有另一个表示2 ^ -1023.
var tests = [1, -1, .123, -.123, 1.5, -1.5, 1e100, -1e100,
1e-100, -1e-100, Infinity, -Infinity];
tests.forEach(function(x)
{
var parts = getNumberParts(x),
value = Math.pow(-1, parts.sign) *
Math.pow(2, parts.exponent) *
parts.mantissa;
console.log("Testing: " + x + " " + value);
console.assert(x === value);
});
console.log("Tests passed");
Run Code Online (Sandbox Code Playgroud)
ECMAScript没有定义任何直接的方法来做到这一点; 但是对于它的价值,这不是一个与分解因子化相同意义上的"分解问题".
理想情况下,您可以通过首先处理符号,然后使用二叉树方法(或对数)来找到指数,然后除以2的相关幂来得到尾数,从而非常快速地完成所需的操作.但不幸的是,在实践中实现这一点可能有点棘手(对于非规范化数字等特殊情况).我建议您阅读ECMAScript规范的第8.5节,以了解您必须处理的情况.
对于此,整数因子分解是不可能的.
指数基本上将是base-2对数的最低点,这并不难计算.
以下代码通过了QuickCheck测试,以及无穷大和负无穷大的测试:
minNormalizedDouble :: Double
minNormalizedDouble = 2 ^^ (-1022)
powers :: [(Int, Double)]
powers = [(b, 2.0 ^^ fromIntegral b) | i <- [9, 8..0], let b = bit i]
exponentOf :: Double -> Int
exponentOf d
| d < 0 = exponentOf (-d)
| d < minNormalizedDouble = -1024
| d < 1 =
let go (dd, accum) (p, twoP)
| dd * twoP < 1 = (dd * twoP, accum - p)
| otherwise = (dd, accum)
in snd $ foldl' go (d, 0) powers
| otherwise =
let go (x, accum) (p, twoP)
| x * twoP <= d = (x * twoP, accum + p)
| otherwise = (x, accum)
in 1 + (snd $ foldl' go (1.0, 0) powers)
decode :: Double -> (Integer, Int)
decode 0.0 = (0, 0)
decode d
| isInfinite d, d > 0 = (4503599627370496, 972)
| isInfinite d, d < 0 = (-4503599627370496, 972)
| isNaN d = (-6755399441055744, 972)
| otherwise =
let
e = exponentOf d - 53
twoE = 2.0 ^^ e
in (round (d / twoE), e)
Run Code Online (Sandbox Code Playgroud)
我使用测试它quickCheck (\ d -> decodeFloat d == decode d),并在正负无穷大上单独测试它.
这里使用的唯一原始操作是左移,双乘,双除,无穷大和NaN测试,Javascript支持我所知.