Donnerstag, 13. Oktober 2011

Binäre Genauigkeit bei der Verwendung von BINARY_DOUBLE

Heute geht's um die Verwendung der Datentypen BINARY_FLOAT bzw. BINARY_DOUBLE und eine mögliche Fehlerquelle. Der folgende anonyme PL/SQL-Block demonstriert das Problem:
declare
 v_value binary_double := 0.0d;
begin
 for i in 1 .. 10 loop
  v_value := v_value + 0.1d;
 end loop;
 if (v_value = 1.0d) then
  dbms_output.put_line('v_value  = 1.0');
 else
  dbms_output.put_line('v_value != 1.0');
 end if;
end;
/
v_value != 1.0
Das Ergebnis verwundert, denn man erwartet, dass 10 mal 0,1 genau 1 ergibt. Die Ursache liegt in der binären Darstellung von 0,1; die Datentypen besitzen eben "nur" eine binäre Genauigkeit.

Statt einem Vergleich auf Gleichheit, der bei Gleitkommazahlen generell nicht zu empfehlen ist, überprüft man, ob die absolute Abweichung zum Vergleichswert sehr gering ist. Die gewählte Genauigkeit ist je nach Anwendung verschieden; zum Beispiel 10-8.

Durch eine Anpassung des vorherigen PL/SQL-Blocks erhält man nun das erwartete Ergebnis:
declare
 v_value binary_double := 0.0d;
begin
 for i in 1 .. 10 loop
  v_value := v_value + 0.1d;
 end loop;
 if (abs(v_value - 1.0d) < 1e-8d) then
  dbms_output.put_line('v_value  = 1.0');
 else
  dbms_output.put_line('v_value != 1.0');
 end if;
end;
/
v_value  = 1.0
Zur Vereinfachung könnte man diese Funktionalität in eine benannte Funktion auslagern:
create or replace function equal(
 p_value_in in binary_double,
 p_comparison_value_in in binary_double,
 p_precision_in in binary_double default 1e-8d
)
return boolean is
begin
 return abs(p_value_in - p_comparison_value_in) < p_precision_in;
end equal;
/

Keine Kommentare:

Kommentar veröffentlichen