[m-dev.] for review: math__solve_quadratic/3

Mark Anthony BROWN dougl at cs.mu.OZ.AU
Wed Nov 1 16:55:22 AEDT 2000


Hi,

The well-known general formula for solving quadratics is prone to
numerical error when there are roots close to zero.  There is a better
formula, but I can never remember it offhand, so I thought it might be
a useful addition to the library.

Cheers,
Mark.

Estimated hours taken: 1

Add a function to the standard library to solve quadratic equations.

library/math.m:
	Export the new function, as well as the type that it returns.

NEWS:
	Mention the new function.

tests/hard_coded/Mmakefile:
tests/hard_coded/solve_quadratic.m:
tests/hard_coded/solve_quadratic.exp:
	Test case for the new feature.

Index: NEWS
===================================================================
RCS file: /home/mercury1/repository/mercury/NEWS,v
retrieving revision 1.176
diff -u -r1.176 NEWS
--- NEWS	2000/10/03 02:21:37	1.176
+++ NEWS	2000/10/31 04:24:02
@@ -124,6 +124,8 @@
 	  truncated to the specified precision, but for ISO C's
 	  printf(), they are rounded rather than being truncated.
 
+* We've added a new function, math__solve_quadratic/3.
+
 Changes to the Mercury implementation:
 
 * We've implemented a new back-end for the Mercury compiler.
Index: library/math.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/math.m,v
retrieving revision 1.24
diff -u -r1.24 math.m
--- library/math.m	1999/12/21 10:33:58	1.24
+++ library/math.m	2000/11/01 03:59:11
@@ -77,7 +77,7 @@
 :- mode math__truncate(in) = out is det.
 
 %---------------------------------------------------------------------------%
-% Power/logarithm operations
+% Polynomial roots
 
 	% math__sqrt(X) = Sqrt is true if Sqrt is the positive square
 	% root of X.
@@ -86,6 +86,21 @@
 :- func math__sqrt(float) = float.
 :- mode math__sqrt(in) = out is det.
 
+:- type math__quadratic_roots
+	--->	no_roots
+	;	one_root(float)
+	;	two_roots(float, float).
+
+	% math__solve_quadratic(A, B, C) = Roots is true if Roots are
+	% the solutions to the equation Ax^2 + Bx + C.
+	%
+	% Domain restriction: A \= 0
+:- func math__solve_quadratic(float, float, float) = quadratic_roots.
+:- mode math__solve_quadratic(in, in, in) = out is det.
+
+%---------------------------------------------------------------------------%
+% Power/logarithm operations
+
 	% math__pow(X, Y) = Res is true if Res is X raised to the
 	% power of Y.
 	%
@@ -187,8 +202,9 @@
 %---------------------------------------------------------------------------%
 
 :- implementation.
+:- import_module float.
 
-% These operations are all implemented using the C interface.
+% These operations are mostly implemented using the C interface.
 
 :- pragma c_header_code("
 
@@ -305,6 +321,49 @@
 #endif
 	SquareRoot = sqrt(X);
 ").
+
+%
+% math__solve_quadratic(A, B, C) = Roots is true if Roots are
+% the solutions to the equation Ax^2 + Bx + C.
+%
+% Domain restrictions:
+% 		A \= 0
+%
+math__solve_quadratic(A, B, C) = Roots :-
+	%
+	% This implementation is designed to minimise numerical errors;
+	% it is adapted from "Numerical recipes in C".
+	%
+	DSquared = B * B - 4.0 * A * C,
+	compare(CmpD, DSquared, 0.0),
+	(
+		CmpD = (<),
+		Roots = no_roots
+	;
+		CmpD = (=),
+		Root = -0.5 * B / A,
+		Roots = one_root(Root)
+	;
+		CmpD = (>),
+		D = sqrt(DSquared),
+		compare(CmpB, B, 0.0),
+		(
+			CmpB = (<),
+			Q = -0.5 * (B - D),
+			Root1 = Q / A,
+			Root2 = C / Q
+		;
+			CmpB = (=),
+			Root1 = -0.5 * D / A,
+			Root2 = -Root1
+		;
+			CmpB = (>),
+			Q = -0.5 * (B + D),
+			Root1 = Q / A,
+			Root2 = C / Q
+		),
+		Roots = two_roots(Root1, Root2)
+	).
 
 %
 % math__pow(X, Y) = Res is true if Res is X raised to the
Index: tests/hard_coded/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/hard_coded/Mmakefile,v
retrieving revision 1.97
diff -u -r1.97 Mmakefile
--- tests/hard_coded/Mmakefile	2000/10/31 02:01:19	1.97
+++ tests/hard_coded/Mmakefile	2000/11/01 04:34:10
@@ -100,6 +100,7 @@
 	rtc_bug \
 	rtti_strings \
 	shift_test \
+	solve_quadratic \
 	space \
 	string_alignment \
 	string_alignment_bug \
Index: tests/hard_coded/solve_quadratic.exp
===================================================================
RCS file: solve_quadratic.exp
diff -N solve_quadratic.exp
--- /dev/null	Tue Jul 25 14:12:01 2000
+++ solve_quadratic.exp	Wed Nov  1 15:24:59 2000
@@ -0,0 +1,16 @@
+Two roots: ok, ok.
+Two roots: ok, ok.
+Two roots: ok, ok.
+Two roots: ok, ok.
+Two roots: ok, ok.
+Two roots: ok, ok.
+One root: ok.
+One root: ok.
+One root: ok.
+No roots.
+No roots.
+No roots.
+Two roots: ok, ok.
+Two roots: ok, ok.
+One root: ok.
+Two roots: ok, ok.
Index: tests/hard_coded/solve_quadratic.m
===================================================================
RCS file: solve_quadratic.m
diff -N solve_quadratic.m
--- /dev/null	Tue Jul 25 14:12:01 2000
+++ solve_quadratic.m	Wed Nov  1 15:31:15 2000
@@ -0,0 +1,75 @@
+:- module solve_quadratic.
+:- interface.
+:- import_module io.
+
+:- pred main(io__state::di, io__state::uo) is det.
+
+:- implementation.
+:- import_module float, math.
+
+main -->
+	% Two roots (i.e. B^2 > 4AC)
+	check_quad(1.0, 3.0, -2.0),
+	check_quad(1.0, -5.0, 3.0),
+	check_quad(2.0, -5.0, 3.0),
+	check_quad(2.0, -5.0, -6.0),
+	check_quad(-15.0, 68.0, 34.5),
+	check_quad(-4.0, -17.0, 3.5),
+
+	% One root (i.e. B^2 = 4AC)
+	check_quad(5.0, 10.0, 5.0),
+	check_quad(4.0, -8.0, 4.0),
+	check_quad(-3.0, -18.0, -27.0),
+
+	% No roots (i.e. B^2 < 4AC)
+	check_quad(4.0, 3.0, 2.0),
+	check_quad(1.0, 1.0, 1.0),
+	check_quad(-1.0, -2.0, -2.0),
+
+	% Special cases
+	check_quad(1.0, -2.0, 0.0),
+	check_quad(1.0, 0.0, -2.0),
+	check_quad(2.0, 0.0, 0.0),
+	check_quad(-100.0, 0.0, 0.0001).
+
+
+:- pred check_quad(float, float, float, io__state, io__state).
+:- mode check_quad(in, in, in, di, uo) is det.
+
+check_quad(A, B, C) -->
+	{ Ss = solve_quadratic(A, B, C) },
+	(
+		{ Ss = no_roots },
+		io__write_string("No roots.\n")
+	;
+		{ Ss = one_root(R) },
+		io__write_string("One root: "),
+		check_result(A, B, C, R),
+		io__write_string(".\n")
+	;
+		{ Ss = two_roots(R1, R2) },
+		io__write_string("Two roots: "),
+		check_result(A, B, C, R1),
+		io__write_string(", "),
+		check_result(A, B, C, R2),
+		io__write_string(".\n")
+	).
+
+:- pred check_result(float, float, float, float, io__state, io__state).
+:- mode check_result(in, in, in, in, di, uo) is det.
+
+check_result(A, B, C, R) -->
+	{ Val = ((A * R + B) * R) + C },
+	(
+		%
+		% This test is pretty conservative, since I don't know
+		% how much error should be expected.
+		%
+		{ abs(Val) < 1E-6 }
+	->
+		io__write_string("ok")
+	;
+		io__write_string("problem: Val = "),
+		io__write_float(Val)
+	).
+
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list