数学函数
浏览 语言基础 主题: ) |
java.lang.Math
类允许使用许多常见的数学函数,这些函数可以在创建程序时使用。
由于它位于 java.lang
包中,因此不需要导入 Math
类。但是,在广泛利用这些函数的程序中,可以使用静态导入。
Math
类中有两个常量,它们是对无理数学数的相当精确的近似值。
Math.E
常量表示 欧拉数 (e) 的值,它是自然对数的底数。
代码部分 3.20:Math.E
public static final double E = 2.718281828459045;
|
Math.PI
常量表示 圆周率 的值,它是圆的周长与其直径的比率。
代码部分 3.21:Math.PI
public static final double PI = 3.141592653589793;
|
Math
类中有一些方法处理指数函数。
幂方法,double Math.pow(double, double)
,返回第一个参数的第二个参数次幂。例如,对 Math.pow(2, 10)
的调用将返回一个值为 1024 (210) 的值。
Math.exp(double)
方法是 pow
的特例,返回 e 的参数次幂。此外,double Math.expm1(double)
返回 (ex - 1)。在这两种特殊情况下,这两种方法都更加准确和方便。
Java 还为 doubles 的平方根和立方根提供了 pow 函数的特例,分别是 double Math.sqrt(double)
和 double Math.cbrt(double)
。
Java 没有通用的对数函数;如果需要,可以使用换底公式模拟。
返回参数的自然对数 (不是常用对数,正如其名称所暗示的!)。double
Math.log(double
)
返回参数的常用(以 10 为底)对数。double
Math.log10(double
)
返回 ln(参数+1)。建议用于小值。double
Math.log1p(double
)
Math
类的三角函数方法允许用户轻松地处理程序中的三角函数。所有方法只接受 double
。请注意,使用这些方法的所有值最初以弧度传递和返回,而不是度。但是,转换是可能的。
三种主要的三角函数方法是 Math.sin(x)
、Math.cos(x)
和 Math.tan(x)
,分别用于查找任何给定数字的正弦、余弦和正切。因此,例如,对 Math.sin(Math.PI/2)
的调用将返回约 1 的值。虽然没有可用于查找余割、正割和余切的方法,但可以通过分别取正弦、余弦和正切的倒数来找到这些值。例如,pi/2 的余割可以使用 1/Math.sin(Math.PI/2)
找到。
Java 提供了三角函数的反函数:Math.asin(x)
和 Math.acos(x)
、Math.atan(x)
。
此外,双曲函数也可用:Math.sinh(x)
、Math.cosh(x)
和 Math.tanh(x)
。
要转换角度的度数和弧度测量值,可以使用两种方法,Math.toRadians(x)
和 Math.toDegrees(x)
。使用 Math.toRadians(x)
时,必须传入一个度数值,并且将返回以弧度表示的值(度数值乘以 pi/180)。Math.toDegrees(x)
方法接收以弧度表示的值,并返回以度数表示的值(弧度值乘以 180/pi)。
Math
类中的绝对值方法与 int
、long
、float
和 double
类型兼容。返回的数据是参数的绝对值(它距零的距离),数据类型相同。例如
代码部分 3.22:Math.abs
int result = Math.abs(-3);
|
在这个例子中,result
将包含值为 3。
最大值和最小值
[edit | edit source]这些方法是比较简单的函数。与其使用 if
...else
语句,不如使用 Math.max(x1, x2)
和 Math.min(x1, x2)
方法。Math.max(x1, x2)
只返回两个值中较大的一个,而 Math.min(x1, x2)
返回两个值中较小的一个。这些方法可接受的类型包括 int
、long
、float
和 double
。
处理浮点表示的函数
[edit | edit source]Java 1.5 和 1.6 引入了几个特定于计算机浮点表示数字的非数学函数。
Math.ulp(
和 double
)Math.ulp(
返回一个 ulp,它是最小的值,当添加到参数中时,会被识别为大于参数的值。float
)
Math.copySign
返回第一个参数的值,其符号与第二个参数的符号相同。它可以用来确定零值的符号。
Math.getExponent
返回(作为 int
)用于在计算机表示中缩放浮点参数的指数。
舍入数字示例
[edit | edit source]有时,我们不仅对数学上正确的舍入数字感兴趣,而且希望始终显示固定数量的有效数字,无论使用什么数字。以下是一个始终返回正确字符串的示例程序。欢迎修改它,使其做到相同,并且更简单!
常量类包含重复的常量,这些常量应该在代码中只存在一次,以避免无意的更改。(如果一个常量被无意地更改,它很可能会被看到,因为它在多个地方被使用。)
代码清单 3.20:StringUtils.java
/**
* Class that comprises of constant values & string utilities.
*
* @since 2013-09-05
* @version 2014-10-14
*/
public class StringUtils {
/** Dash or minus constant */
public static final char DASH = '-';
/** The exponent sign in a scientific number, or the capital letter E */
public static final char EXPONENT = 'E';
/** The full stop or period */
public static final char PERIOD = '.';
/** The zero string constant used at several places */
public static final String ZERO = "0";
/**
* Removes all occurrences of the filter character in the text.
*
* @param text Text to be filtered
* @param filter The character to be removed.
* @return the string
*/
public static String filter(final String text, final String filter) {
final String[] words = text.split("[" + filter + "]");
switch (words.length) {
case 0: return text;
case 1: return words[0];
default:
final StringBuilder filteredText = new StringBuilder();
for (final String word : words) {
filteredText.append(word);
}
return filteredText.toString();
}
}
}
|
MathsUtils 类是对 java.lang.Math
类的补充,包含舍入计算。
代码清单 3.21:MathsUtils.java
package string;
/**
* Class for special mathematical calculations.<br/>
* ATTENTION:<br/>Should depend only on standard Java libraries!
*
* @since 2013-09-05
* @version 2014-10-14
*/
public class MathsUtils {
// CONSTANTS
// ------------------------------------------
/** The exponent sign in a scientific number, or the capital letter E. */
public static final char EXPONENT = 'E';
/** Value after which the language switches from scientific to double */
private static final double E_TO_DOUBLE = 1E-3;
/** The zero string constant used at several places. */
public static final String ZERO = "0";
/** The string of zeros */
private static final String ZEROS = "000000000000000000000000000000000";
// METHODS
// ------------------------------------------
/**
* Determines, if the number uses a scientific representation.
*
* @param number the number
* @return true, if it is a scientific number, false otherwise
*/
private static boolean isScientific(final double number) {
return ((new Double(number)).toString().indexOf(EXPONENT) > 0);
}
/**
* Determines how many zeros are to be appended after the decimal digits.
*
* @param significantsAfter Requested significant digits after decimal
* @param separator Language-specific decimal separator
* @param number Rounded number
* @return Requested value
*/
private static byte calculateMissingSignificantZeros(
final byte significantsAfter,
final char separator,
final double number) {
final byte after = findSignificantsAfterDecimal(separator, number);
final byte zeros =
(byte) (significantsAfter - ((after == 0) ? 1 : after));
return ((zeros >= 0) ? zeros : 0);
}
/**
* Finds the insignificant zeros after the decimal separator.
*
* @param separator Language-specific decimal separator
* @param number the number
* @return the byte
*/
private static byte findInsignificantZerosAfterDecimal(
final char separator,
final double number) {
if ((Math.abs(number) >= 1) || isScientific(number)) {
return 0;
} else {
final StringBuilder string = new StringBuilder();
string.append(number);
string.delete(0,
string.indexOf(new Character(separator).toString()) + 1);
// Determine what to match:
final String regularExpression = "[1-9]";
final String[] split = string.toString().split(regularExpression);
return (split.length > 0) ? (byte) split[0].length() : 0;
}
}
/**
* Calculates the number of all significant digits (without the sign and
* the decimal separator).
*
* @param significantsAfter Requested significant digits after decimal
* @param separator Language-specific decimal separator
* @param number Value where the digits are to be counted
* @return Number of significant digits
*/
private static byte findSignificantDigits(final byte significantsAfter,
final char separator,
final double number) {
if (number == 0) { return 0; }
else {
String mantissa =
findMantissa(separator, new Double(number).toString());
if (number == (long)number) {
mantissa = mantissa.substring(0, mantissa.length() - 1);
}
mantissa = retrieveDigits(separator, mantissa);
// Find the position of the first non-zero digit:
short nonZeroAt = 0;
for (; (nonZeroAt < mantissa.length())
&& (mantissa.charAt(nonZeroAt) == '0'); nonZeroAt++) ;
return (byte)mantissa.substring(nonZeroAt).length();
}
}
/**
* Determines the number of significant digits after the decimal separator
* knowing the total number of significant digits and the number before the
* decimal separator.
*
* @param significantsBefore Number of significant digits before separator
* @param significantDigits Number of all significant digits
* @return Number of significant decimals after the separator
*/
private static byte findSignificantsAfterDecimal(
final byte significantsBefore,
final byte significantDigits) {
final byte afterDecimal =
(byte) (significantDigits - significantsBefore);
return (byte) ((afterDecimal > 0) ? afterDecimal : 0);
}
/**
* Determines the number of digits before the decimal point.
*
* @param separator Language-specific decimal separator
* @param number Value to be scrutinised
* @return Number of digits before the decimal separator
*/
private static byte findSignificantsBeforeDecimal(final char separator,
final double number) {
final String value = new Double(number).toString();
// Return immediately, if result is clear: Special handling at
// crossroads of floating point and exponential numbers:
if ((number == 0) || (Math.abs(number) >= E_TO_DOUBLE)
&& (Math.abs(number) < 1)) {
return 0;
} else if ((Math.abs(number) > 0) && (Math.abs(number) < E_TO_DOUBLE)) {
return 1;
} else {
byte significants = 0;
// Significant digits to the right of decimal separator:
for (byte b = 0; b < value.length(); b++) {
if (value.charAt(b) == separator) {
break;
} else if (value.charAt(b) != StringUtils.DASH) {
significants++;
}
}
return significants;
}
}
/**
* Returns the exponent part of the double number.
*
* @param number Value of which the exponent is of interest
* @return Exponent of the number or zero.
*/
private static short findExponent(final double number) {
return new Short(findExponent((new Double(number)).toString()));
}
/**
* Finds the exponent of a number.
*
* @param value Value where an exponent is to be searched
* @return Exponent, if it exists, or "0".
*/
private static String findExponent(final String value) {
final short exponentAt = (short) value.indexOf(EXPONENT);
if (exponentAt < 0) { return ZERO; }
else {
return value.substring(exponentAt + 1);
}
}
/**
* Finds the mantissa of a number.
*
* @param separator Language-specific decimal separator
* @param value Value where the mantissa is to be found
* @return Mantissa of the number
*/
private static String findMantissa(final char separator,
final String value) {
String strValue = value;
final short exponentAt = (short) strValue.indexOf(EXPONENT);
if (exponentAt > -1) {
strValue = strValue.substring(0, exponentAt);
}
return strValue;
}
/**
* Retrieves the digits of the value without decimal separator or sign.
*
* @param separator
* @param number Mantissa to be scrutinised
* @return The digits only
*/
private static String retrieveDigits(final char separator, String number) {
// Strip off exponent part, if it exists:
short eAt = (short)number.indexOf(EXPONENT);
if (eAt > -1) {
number = number.substring(0, eAt);
}
return number.replace((new Character(StringUtils.DASH)).toString(), "").
replace((new Character(separator)).toString(), "");
}
// ---- Public methods ----------------------
/**
* Returns the number of digits in the long value.
*
* @param value the value
* @return the byte
*/
public static byte digits(final long value) {
return (byte) StringUtils.filter(Long.toString(value), ".,").length();
}
/**
* Finds the significant digits after the decimal separator of a mantissa.
*
* @param separator Language-specific decimal separator
* @param number Value to be scrutinised
* @return Number of significant zeros after decimal separator.
*/
public static byte findSignificantsAfterDecimal(final char separator,
final double number) {
if (number == 0) { return 1; }
else {
String value = (new Double(number)).toString();
final short separatorAt = (short) value.indexOf(separator);
if (separatorAt > -1) {
value = value.substring(separatorAt + 1);
}
final short exponentAt = (short) value.indexOf(EXPONENT);
if (exponentAt > 0) {
value = value.substring(0, exponentAt);
}
final Long longValue = new Long(value).longValue();
if (Math.abs(number) < 1) {
return (byte) longValue.toString().length();
} else if (longValue == 0) {
return 0;
} else {
return (byte) (("0." + value).length() - 2);
}
}
}
/**
* Calculates the power of the base to the exponent without changing the
* least-significant digits of a number.
*
* @param basis
* @param exponent
* @return basis to power of exponent
*/
public static double power(final int basis, final short exponent) {
return power((short) basis, exponent);
}
/**
* Calculates the power of the base to the exponent without changing the
* least-significant digits of a number.
*
* @param basis the basis
* @param exponent the exponent
* @return basis to power of exponent
*/
public static double power(final short basis, final short exponent) {
if (basis == 0) {
return (exponent != 0) ? 1 : 0;
} else {
if (exponent == 0) {
return 1;
} else {
// The Math method power does change the least significant
// digits after the decimal separator and is therefore useless.
double result = 1;
short s = 0;
if (exponent > 0) {
for (; s < exponent; s++) {
result *= basis;
}
} else if (exponent < 0) {
for (s = exponent; s < 0; s++) {
result /= basis;
}
}
return result;
}
}
}
/**
* Rounds a number to the decimal places.
*
* @param significantsAfter Requested significant digits after decimal
* @param separator Language-specific decimal separator
* @param number Number to be rounded
* @return Rounded number to the requested decimal places
*/
public static double round(final byte significantsAfter,
final char separator,
final double number) {
if (number == 0) { return 0; }
else {
final double constant = power(10, (short)
(findInsignificantZerosAfterDecimal(separator, number)
+ significantsAfter));
final short dExponent = findExponent(number);
short exponent = dExponent;
double value = number*constant*Math.pow(10, -exponent);
final String exponentSign =
(exponent < 0) ? String.valueOf(StringUtils.DASH) : "";
if (exponent != 0) {
exponent = (short) Math.abs(exponent);
value = round(value);
} else {
value = round(value)/constant;
}
// Power method cannot be used, as the exponentiated number may
// exceed the maximal long value.
exponent -= Math.signum(dExponent)*(findSignificantDigits
(significantsAfter, separator, value) - 1);
if (dExponent != 0) {
String strValue = Double.toString(value);
strValue = strValue.substring(0, strValue.indexOf(separator))
+ EXPONENT + exponentSign + Short.toString(exponent);
value = new Double(strValue);
}
return value;
}
}
/**
* Rounds a number according to mathematical rules.
*
* @param value the value
* @return the double
*/
public static double round(final double value) {
return (long) (value + .5);
}
/**
* Rounds to a fixed number of significant digits.
*
* @param significantDigits Requested number of significant digits
* @param separator Language-specific decimal separator
* @param dNumber Number to be rounded
* @return Rounded number
*/
public static String roundToString(final byte significantDigits,
final char separator,
double dNumber) {
// Number of significants that *are* before the decimal separator:
final byte significantsBefore =
findSignificantsBeforeDecimal(separator, dNumber);
// Number of decimals that *should* be after the decimal separator:
final byte significantsAfter = findSignificantsAfterDecimal(
significantsBefore, significantDigits);
// Round to the specified number of digits after decimal separator:
final double rounded = MathsUtils.round(significantsAfter, separator, dNumber);
final String exponent = findExponent((new Double(rounded)).toString());
final String mantissa = findMantissa(separator,
(new Double(rounded)).toString());
final double dMantissa = new Double(mantissa).doubleValue();
final StringBuilder result = new StringBuilder(mantissa);
// Determine the significant digits in this number:
final byte significants = findSignificantDigits(significantsAfter,
separator, dMantissa);
// Add lagging zeros, if necessary:
if (significants <= significantDigits) {
if (significantsAfter != 0) {
result.append(ZEROS.substring(0,
calculateMissingSignificantZeros(significantsAfter,
separator, dMantissa)));
} else {
// Cut off the decimal separator & after decimal digits:
final short decimal = (short) result.indexOf(
new Character(separator).toString());
if (decimal > -1) {
result.setLength(decimal);
}
}
} else if (significantsBefore > significantDigits) {
dNumber /= power(10, (short) (significantsBefore - significantDigits));
dNumber = round(dNumber);
final short digits =
(short) (significantDigits + ((dNumber < 0) ? 1 : 0));
final String strDouble = (new Double(dNumber)).toString().substring(0, digits);
result.setLength(0);
result.append(strDouble + ZEROS.substring(0,
significantsBefore - significantDigits));
}
if (new Short(exponent) != 0) {
result.append(EXPONENT + exponent);
}
return result.toString();
} // public static String roundToString(…)
/**
* Rounds to a fixed number of significant digits.
*
* @param separator Language-specific decimal separator
* @param significantDigits Requested number of significant digits
* @param value Number to be rounded
* @return Rounded number
*/
public static String roundToString(final char separator,
final int significantDigits,
float value) {
return roundToString((byte)significantDigits, separator,
(double)value);
}
} // class MathsUtils
|
代码使用以下 JUnit 测试进行测试
代码清单 3.22:MathsUtilsTest.java
package string;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Vector;
import org.junit.Test;
/**
* The JUnit test for the <code>MathsUtils</code> class.
*
* @since 2013-03-26
* @version 2014-10-14
*/
public class MathsUtilsTest {
/**
* Method that adds a negative and a positive value to values.
*
* @param d the double value
* @param values the values
*/
private static void addValue(final double d, Vector<Double> values) {
values.add(-d);
values.add(d);
}
// Public methods ------
/**
* Tests the round method with a double parameter.
*/
@Test
public void testRoundToStringDoubleByteCharDouble() {
// Test rounding
final Vector<Double> values = new Vector<Double>();
final Vector<String> strValues = new Vector<String>();
values.add(0.0);
strValues.add("0.00000");
addValue(1.4012984643248202e-45, values);
strValues.add("-1.4012E-45");
strValues.add("1.4013E-45");
addValue(1.999999757e-5, values);
strValues.add("-1.9999E-5");
strValues.add("2.0000E-5");
addValue(1.999999757e-4, values);
strValues.add("-1.9999E-4");
strValues.add("2.0000E-4");
addValue(1.999999757e-3, values);
strValues.add("-0.0019999");
strValues.add("0.0020000");
addValue(0.000640589, values);
strValues.add("-6.4058E-4");
strValues.add("6.4059E-4");
addValue(0.3396899998188019, values);
strValues.add("-0.33968");
strValues.add("0.33969");
addValue(0.34, values);
strValues.add("-0.33999");
strValues.add("0.34000");
addValue(7.07, values);
strValues.add("-7.0699");
strValues.add("7.0700");
addValue(118.188, values);
strValues.add("-118.18");
strValues.add("118.19");
addValue(118.2, values);
strValues.add("-118.19");
strValues.add("118.20");
addValue(123.405009, values);
strValues.add("-123.40");
strValues.add("123.41");
addValue(30.76994323730469, values);
strValues.add("-30.769");
strValues.add("30.770");
addValue(130.76994323730469, values);
strValues.add("-130.76");
strValues.add("130.77");
addValue(540, values);
strValues.add("-539.99");
strValues.add("540.00");
addValue(12345, values);
strValues.add("-12344");
strValues.add("12345");
addValue(123456, values);
strValues.add("-123450");
strValues.add("123460");
addValue(540911, values);
strValues.add("-540900");
strValues.add("540910");
addValue(9.223372036854776e56, values);
strValues.add("-9.2233E56");
strValues.add("9.2234E56");
byte i = 0;
final byte significants = 5;
for (final double element : values) {
final String strValue;
try {
strValue = MathsUtils.roundToString(significants, StringUtils.PERIOD, element);
System.out.println(" MathsUtils.round(" + significants + ", '"
+ StringUtils.PERIOD + "', " + element + ") ==> "
+ strValue + " = " + strValues.get(i));
assertEquals("Testing roundToString", strValue, strValues.get(i++));
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} // class MathsUtilsTest
|
JUnit 测试的输出如下
代码清单 3.22 的输出
MathsUtils.round(5, '.', 0.0) ==> 0.00000 = 0.00000 MathsUtils.round(5, '.', -1.4012984643248202E-45) ==> -1.4012E-45 = -1.4012E-45 MathsUtils.round(5, '.', 1.4012984643248202E-45) ==> 1.4013E-45 = 1.4013E-45 MathsUtils.round(5, '.', -1.999999757E-5) ==> -1.9999E-5 = -1.9999E-5 MathsUtils.round(5, '.', 1.999999757E-5) ==> 2.0000E-5 = 2.0000E-5 MathsUtils.round(5, '.', -1.999999757E-4) ==> -1.9999E-4 = -1.9999E-4 MathsUtils.round(5, '.', 1.999999757E-4) ==> 2.0000E-4 = 2.0000E-4 MathsUtils.round(5, '.', -0.001999999757) ==> -0.0019999 = -0.0019999 MathsUtils.round(5, '.', 0.001999999757) ==> 0.0020000 = 0.0020000 MathsUtils.round(5, '.', -6.40589E-4) ==> -6.4058E-4 = -6.4058E-4 MathsUtils.round(5, '.', 6.40589E-4) ==> 6.4059E-4 = 6.4059E-4 MathsUtils.round(5, '.', -0.3396899998188019) ==> -0.33968 = -0.33968 MathsUtils.round(5, '.', 0.3396899998188019) ==> 0.33969 = 0.33969 MathsUtils.round(5, '.', -0.34) ==> -0.33999 = -0.33999 MathsUtils.round(5, '.', 0.34) ==> 0.34000 = 0.34000 MathsUtils.round(5, '.', -7.07) ==> -7.0699 = -7.0699 MathsUtils.round(5, '.', 7.07) ==> 7.0700 = 7.0700 MathsUtils.round(5, '.', -118.188) ==> -118.18 = -118.18 MathsUtils.round(5, '.', 118.188) ==> 118.19 = 118.19 MathsUtils.round(5, '.', -118.2) ==> -118.19 = -118.19 MathsUtils.round(5, '.', 118.2) ==> 118.20 = 118.20 MathsUtils.round(5, '.', -123.405009) ==> -123.40 = -123.40 MathsUtils.round(5, '.', 123.405009) ==> 123.41 = 123.41 MathsUtils.round(5, '.', -30.76994323730469) ==> -30.769 = -30.769 MathsUtils.round(5, '.', 30.76994323730469) ==> 30.770 = 30.770 MathsUtils.round(5, '.', -130.7699432373047) ==> -130.76 = -130.76 MathsUtils.round(5, '.', 130.7699432373047) ==> 130.77 = 130.77 MathsUtils.round(5, '.', -540.0) ==> -539.99 = -539.99 MathsUtils.round(5, '.', 540.0) ==> 540.00 = 540.00 MathsUtils.round(5, '.', -12345.0) ==> -12344 = -12344 MathsUtils.round(5, '.', 12345.0) ==> 12345 = 12345 MathsUtils.round(5, '.', -123456.0) ==> -123450 = -123450 MathsUtils.round(5, '.', 123456.0) ==> 123460 = 123460 MathsUtils.round(5, '.', -540911.0) ==> -540900 = -540900 MathsUtils.round(5, '.', 540911.0) ==> 540910 = 540910 MathsUtils.round(5, '.', -9.223372036854776E56) ==> -9.2233E56 = -9.2233E56 MathsUtils.round(5, '.', 9.223372036854776E56) ==> 9.2234E56 = 9.2234E56 |
如果您对与 C# 的比较感兴趣,请查看那里的 舍入数字示例。如果您对与 C++ 的比较感兴趣,您可以将此处的代码与那里的相同 示例 进行比较。
请注意,在以 if ((D == 0)
开头的表达式中,由于源模板中的错误,我必须使用 OR 而不是 ||
。