2014-12-26 8 views
6

EinführungErhalten nächste kleinste nächste Zahl in eine Dezimalzahl

Für einige Berechnungen Ich brauche eine möglichst kleine Zahl zu finden, den ich von einer bestimmten Anzahl addieren/subtrahieren ohne JavaScript in Schwierigkeiten mit dem internen verwendeten Datentyp zu bekommen.

Tor

Ich habe versucht, eine Funktion zu schreiben, die die nächste nächste Nummer VALUE in Richtung Wert DIR zurückkehren kann.

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; 
    } 
} 

Das Problem dabei ist, dass JavaScript einen 64-Bit-Float-Typen verwendet (glaube ich), die unterschiedlichen Mindestschrittgrößen abhängig von seinem aktuellen Exponenten hat.

Problem im Detail

Das Problem ist die Größe Schritt auf seinem aktuellen Exponenten abhängig:

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) 

Zusammenfassung

So wie kann ich die kleinstmögliche Zahl finde ich kann Addieren/Subtrahieren von einem Wert, der in einem Parameter in einer beliebigen Richtung angegeben ist? In C++ wäre dies leicht durch Zugriff auf die Zahlen Binärwerte möglich.

+3

Sie Vorteil von [typisierte Arrays] nehmen können (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays) am niedrig- zu bekommen Ebenendarstellung von Fließkommawerten in JavaScript. Erstellen Sie ein 1-Element-Float64Array- und ein 8-Element-UInt8-Array über denselben Puffer. – Pointy

Antwort

5

Ich habe versucht, Pointy's Vorschlag aus den Kommentaren zu implementieren (mit typisierten Arrays). Dies ist lose an die glibc Implementierung von nextafter angepasst. Sollte gut genug sein.

Sie can actually just increment/decrement die 64-Bit-Integer-Darstellung eines Double, um das gewünschte Ergebnis zu erhalten. Ein Mantissenüberlauf wird zu dem Exponenten überlaufen, der zufällig genau das ist, was Sie wollen.

Da JavaScript keine Uint64Array bietet, musste ich einen manuellen Überlauf über zwei 32-Bit-Ganzzahlen implementieren.

Dies funktioniert auf Little-Endian-Architekturen, aber ich habe Big-Endian weggelassen, da ich keine Möglichkeit habe, es zu testen. Wenn Sie dies für Big-Endian-Architekturen benötigen, müssen Sie diesen Code anpassen.

// 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()' hat einen 'isLittleEndian'-Parameter, der helfen könnte. http://www.ecma-international.org/ecma-262/6.0/#sec-dataview.prototype.getfloat64 –

0

Number.MIN_VALUE ist die kleinstmögliche darstellbare Zahl, nicht der kleinstmögliche Unterschied zwischen darstellbaren Zahlen. Aufgrund der Art und Weise, wie javascript Fließkommazahlen verarbeitet, ändert sich der kleinstmögliche Unterschied zwischen den darstellbaren Zahlen mit der Größe der Zahl. Wenn die Zahl größer wird, wird die Genauigkeit kleiner. Daher gibt es keine Nummer, die Ihr Problem lösen könnte. Ich schlage vor, dass Sie entweder überdenken, wie Sie Ihr Problem lösen oder eine Teilmenge der zu verwendenden Zahlen wählen und nicht den vollen Bereich der MAX- und MIN-Werte.

zum Beispiel: 1.7976931348623156e+308 == 1.7976931348623155e+308 //true

Auch durch MIN_VALUE von MAX_VALUE subtrahieren Sie versuchen, Javascript zu bekommen, um genau eine Zahl mit mehr als 600 bedeutenden Persönlichkeiten darstellen. Das ist ein bisschen zu viel.

+2

Sie erklären hier nur das OP-Problem. Für jede endliche Zahl "x" gibt es eine kleinste Zahl "e" wie "x + e! = E ||" x-e! = e'. Das OP fragt, wie man 'e' für eine gegebene Zahl' x' findet. –

+0

Sicher, aber es gibt keine Antwort auf dieses Problem, das vernünftig erscheint. Oben bei Max_Value brauchst du mindestens 2. Down bei min Wert wenn du 2 addierst bekommst du 2. Damit verlierst du effektiv alle Unterschiede zwischen sehr kleinen Zahlen und musst ganze Zahlen mit den sehr großen überspringen. Dieses Problem hat keine nützliche Lösung, die eine gleitende Skala von Werten nicht enthält. – greggreg

+0

Ja, das versuche ich zu sagen. Meine * Nummer * 'e' oben ist wirklich eine Funktion ... Schreibe eine' Funktion epsilon (x) 'wie' x + epsilon (x)! = X'. Das * epsilon * hängt von der Skala von 'x' ab. –

Verwandte Themen