Genauigkeit von System.Double im Zusammenspiel mit DirectX
Beim Prüfen von Quelltext sind mir wiederholt Ungenauigkeiten mit double Zahlen aufgefallen, die schon in der 8. Stelle auftraten. Nachdem ich Ausgeschlossen hatte, dass irgendwo Single-Konvertierungen stattfinden, habe ich das Problem genauer unter die Lupe genommen. Konkret ist mir aufgefallen, dass die double Rechnung 4.0 * 25.4 eigentlich 101.6 ergeben müsste, in der Zielvariable stand aber 101.59999847412109. Extern nachstellbar war das Problem anfangs nicht.
Reproduzieren ließ sich das Verhalten erst mit folgender Funktion:
private void TestDouble() { var pres = new Microsoft.DirectX.Direct3D.PresentParameters { Windowed = true, SwapEffect = Microsoft.DirectX.Direct3D.SwapEffect.Discard, AutoDepthStencilFormat = Microsoft.DirectX.Direct3D.DepthFormat.D16 }; new Microsoft.DirectX.Direct3D.Device( 0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, Microsoft.DirectX.Direct3D.CreateFlags.SoftwareVertexProcessing, pres); double value = 4; double value2 = value * 25.4; Debug.Assert(101.6 == value2); } |
Das Erzeugen der 3D-Welt mittel Managed DirectX setzt standartmäßig die Genauigkeit von Double-Zahlen auf einfache Genauigkeit (entspricht Single). Nachdem diese Ursache gefunden war, fand ich hier:
http://blogs.msdn.com/b/tmiller/archive/2004/06/01/145596.aspx
schnell eine Problemlösung. Wenn man mit leichten Geschwindigkeitseinschränkungen leben kann, ist nur eine kleine Änderung beim Erzeugen vom Device notwendig:
new Microsoft.DirectX.Direct3D.Device( 0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, Microsoft.DirectX.Direct3D.CreateFlags.SoftwareVertexProcessing | Microsoft.DirectX.Direct3D.CreateFlags.FpuPreserve, pres); |
Der zusätzliche Parameter Microsoft.DirectX.Direct3D.CreateFlags.FpuPreserve sorgt dafür, dass DirectX die Genauigkeit der Double-Zahlen so lässt, wie das Standardmäßig der Fall ist.