2
\$\begingroup\$

Utility to calculate the square root of a number. The method also accept an epsilon value, which controls the precision. The epsilon value could range to any number including zero. I am expecting a general code review or if there is a better way to write the code.

/**
 * The class MathUtil contains methods for performing basic numeric operations 
 such as the squareRoot functions. Square root of 2.0: 1.4142135623746899
 */
public class MathUtil {
 /**
 * Returns the correctly rounded positive square root of a double value.
 * Special cases: If the argument is NaN or less than zero, then the result
 * is NaN. If the argument is positive zero or negative zero, then the
 * result is the same as the argument. Otherwise, the result is the double
 * value closest to the epsilon.
 * 
 * Function takes a non-negative real number * 
 * @param a double
 * @param epsilon double
 * @return double
 */
 public static double squareRoot(final double a, final double epsilon) {
 if (a == 0)
 return a;
 else
 return internalSqrRoot(a, a / 2, epsilon);
 }
 /**
 * Method internalSqrRoot.
 * 
 * @param a double
 * @param x double
 * @param epsilon double
 * @return double
 */
 public static double internalSqrRoot(final double a, final double x,
 final double epsilon) {
 if (closeEnough(a, x, epsilon)) {
 return x;
 } else {
 return internalSqrRoot(a, betterGuess(a, x), epsilon);
 }
 }
 /*
 * return true if the current guess is close enought o accepted value
 * Method closeEnough.
 * 
 * @param a double
 * @param x double
 * @param epsilon double
 * @return boolean
 */
 public static boolean closeEnough(double a, double x, double epsilon) {
 return (Math.abs(x - ((a / x) + x) / 2)) <= epsilon;
 }
 /*
 * perform a simple manipulation to get a better guess (one closer to the
 * actual square root) by averaging y with x/y
 * Method betterGuess.
 * @param a double
 * @param x double
 * @return double
 */
 public static double betterGuess(double a, double x) {
 return ((a / x) + x) / 2;
 }
}
200_success
146k22 gold badges190 silver badges479 bronze badges
asked May 12, 2015 at 17:44
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Duplicated logic

Avoid duplicated logic like this:

public static boolean closeEnough(double a, double x, double epsilon) {
 return (Math.abs(x - ((a / x) + x) / 2)) <= epsilon;
}
public static double betterGuess(double a, double x) {
 return ((a / x) + x) / 2;
}

The closeEnough method includes the exact same logic as betterGuess. Make it a habit to look at duplicated code fragments with suspicion.

If you eliminate the duplication, the code becomes:

public static boolean closeEnough(double a, double x, double epsilon) {
 return (Math.abs(x - betterGuess(a, x))) <= epsilon;
}
public static double betterGuess(double a, double x) {
 return ((a / x) + x) / 2;
}

... but then, does this actually make sense? Calling betterGuess from closeEnough? Not really. How can you check if a candidate square root is close enough? You cannot compare with the real square root, because that's the target unknown. What you can compare with, is the original number, which you should be able to get by squaring:

public static boolean closeEnough(double a, double x, double epsilon) {
 return Math.abs(a - x * x) <= epsilon;
}

This implementation is more logical, and has another positive side effect: it makes it possible to clean up the squareRoot method with the suspicious 0-check:

public static double squareRoot(final double a, final double epsilon) {
 if (a == 0)
 return a;
 else
 return internalSqrRoot(a, a / 2, epsilon);
}

The method can become simply:

public static double squareRoot(final double a, final double epsilon) {
 return internalSqrRoot(a, a / 2, epsilon);
}

It would seem the 0-check was there to prevent a division by zero problem in Math.abs(x - ((a / x) + x) / 2))

Method visibility

The MathUtil class name suggests it's a utility class, but the public MathUtil.betterGuess method (to name just one) doesn't seem very useful. It's an implementation detail that should be hidden, so make it private. Question the other methods too, and change their visibility as appropriate.

The variable names are quite poor. For example in closeEnough it's impossible to tell if the target number is a or x. target and candidate might have been better names.

Input validation

More than just commenting "Function takes a non-negative real number ", it would be better to enforce that by throwing an IllegalArgumentException.

Poor JavaDoc

This kind of JavaDoc is worse than no JavaDoc at all:

/**
 * Method internalSqrRoot.
 *
 * @param a double
 * @param x double
 * @param epsilon double
 * @return double
 */

It's worse, because tells nothing new about the method, but since it's there, I've read it, in vain.

Minor things

  • It's recommended to use braces even with single-line if statements
  • Redundant parentheses in the expression (Math.abs(x - ((a / x) + x) / 2))
  • Perhaps instead of internalSqrRoot, squareRootHelper might be a better name. Or, since the method has a different signature than squareRoot, it can just as well be squareRoot (overloading)
answered May 12, 2015 at 22:17
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.