2014-12-26 42 views
6

介紹下一個最小最接近的數字得到一個小數

對於某些計算,我需要找到儘可能小的號碼,我可以從指定數量的加/減沒有JavaScript越來越麻煩與內部使用的數據類型。

目標

我試圖寫的函數,其能夠到下一個最接近的數字返回值的價值DIR的方向。

function nextNearest(value, direction) { 
    // Special cases for value==0 or value==direction removed 

    if (direction < value) { 
     return value - Number.MIN_VALUE; 
    } else { 
     return value + Number.MIN_VALUE; 
    } 
} 

問題與此是,JavaScript使用根據其當前指數,其具有不同的最小步長64位浮點類型(我想)。

詳細

問題問題是步長取決於其當前指數:

var a = Number.MIN_VALUE; 

console.log(a); 
// 5e-324 

console.log(a + Number.MIN_VALUE); 
// 1e-323 (changed, as expected) 


var a = Number.MAX_VALUE; 

console.log(a); 
// 1.7976931348623157e+308 

console.log(a - Number.MIN_VALUE); 
// 1.7976931348623157e+308 (that's wrong) 

console.log(a - Number.MIN_VALUE == a); 
// true (which also is wrong) 

摘要

所以,我怎麼能找到最小的數,我可以從任何方向的參數中指定的值加/減?在C++中,通過訪問數字二進制值很容易實現。

+3

您可以利用的[類型數組(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays)來獲得在低JavaScript中浮點值的高級表示。在同一個緩衝區上創建一個1元素的Float64Array和一個8元素的UInt8數組。 – Pointy

回答

5

我試圖從評論(使用類型化數組)中實現Pointy的建議。這是從glibc的執行nextafter鬆散地改編。應該足夠好。

can actually just increment/decrement 64位整數表示雙重獲得想要的結果。尾數溢出會溢出到指數上,這恰好就是你想要的。

由於JavaScript不提供Uint64Array我不得不實施手動溢出兩個32位整數。

這適用於小端架構,但由於我無法測試它,所以我忽略了big-endian。如果你需要這個工作在big-endian體系結構上,你必須修改這些代碼。

// Return the next representable double from value towards direction 
 
function nextNearest(value, direction) { 
 
    if (typeof value != "number" || typeof direction != "number") 
 
    return NaN; 
 
    
 
    if (isNaN(value) || isNaN(direction)) 
 
    return NaN; 
 
    
 
    if (!isFinite(value)) 
 
    return value; 
 
    
 
    if (value === direction) 
 
    return value; 
 
    
 
    var buffer = new ArrayBuffer(8); 
 
    var f64 = new Float64Array(buffer); 
 
    var u32 = new Uint32Array(buffer); 
 
    
 
    f64[0] = value; 
 
    
 
    if (value === 0) { 
 
    u32[0] = 1; 
 
    u32[1] = direction < 0 ? 1 << 31 : 0; 
 
    } else if ((value > 0) && (value < direction) || (value < 0) && (value > direction)) { 
 
    if (u32[0]++ === 0xFFFFFFFF) 
 
     u32[1]++; 
 
    } else { 
 
    if (u32[0]-- === 0) 
 
     u32[1]--; 
 
    } 
 
    
 
    return f64[0]; 
 
} 
 

 
var testCases = [0, 1, -1, 0.1, 
 
       -1, 10, 42e42, 
 
       0.9999999999999999, 1.0000000000000002, 
 
       10.00000762939453, // overflows between dwords 
 
       5e-324, -5e-324, // minimum subnormals (around zero) 
 
       Number.MAX_VALUE, -Number.MAX_VALUE, 
 
       Infinity, -Infinity, NaN]; 
 

 
document.write("<table><tr><th>n</th><th>next</th><th>prev</th></tr>"); 
 
testCases.forEach(function(n) { 
 
    var next = nextNearest(n, Infinity); 
 
    var prev = nextNearest(n, -Infinity); 
 
    document.write("<tr><td>" + n + "</td><td>" + next + "</td><td>" + prev + "</td></tr>"); 
 
}); 
 
document.write("</table>");

+1

'DataView.getFloat()'有一個'isLittleEndian'參數可能有幫助。 http://www.ecma-international.org/ecma-262/6.0/#sec-dataview.prototype.getfloat64 –

0

Number.MIN_VALUE是可能的最小可表示數字,而不是可表示數字之間可能的最小差異。由於JavaScript處理浮點數的方式,可表示數字之間的最小可能差異隨數字大小而變化。隨着數量越來越大,精度越來越小。因此沒有任何一個數字可以解決你的問題。我建議你重新思考你將如何解決你的問題,或者選擇一個數字的子集來使用,而不是全部的MAX和MIN值。

例如:1.7976931348623156e+308 == 1.7976931348623155e+308 //true

此外,通過減去MIN_VALUE從MAX_VALUE你試圖讓JavaScript來精確表示有超過600顯著數字一個數字。這太過分了。

+2

你只是在這裏解釋OP的問題。對於每個有限數字「x」,都有一個最小數字「e」,例如「x + e!= e ||」 x-e!= e'。 OP在詢問如何爲給定數字'x'找到'e'。 –

+0

當然,但這個問題似乎沒有答案,似乎是合理的。在Max_Value上,你至少需要2個。如果你加上2,那麼你至少需要2個。所以你有效地失去了非常小的數字之間的所有區別,並且必須跳過非常大的整數。此問題沒有有用的解決方案,不包含值的滑動比例。 – greggreg

+0

是的,這就是我想說的。上面的我的*號*'e'實際上是一個函數...編寫一個'函數epsilon(x)',如'x + epsilon(x)!= x'。 * epsilon *取決於'x'的比例。 –

相關問題