1.四舍五入实数

正如我们已经讨论过的,当一个实数被分配给一个int变量时,它总是被四舍五入到最接近的更小的整数——小数部分被简单地丢弃。

但是很容易想象这样一种情况,当一个小数需要在任一方向上四舍五入到最接近的整数,甚至四舍五入。在这种情况下你会怎么做?

对于这种情况和许多类似的情况,Java 有类Math,它有round()ceil()floor()方法。


Math.round()方法

Math.round()方法将数字四舍五入为最接近的整数:

long x = Math.round(real_number)

但这里还有另一个细微差别:此方法返回一个long整数(不是int)。因为实数可能非常大,Java 的创建者决定使用 Java 最大的可用整数类型:long.

因此,如果程序员想要将结果分配给一个int变量,那么她必须明确地向编译器表明她接受可能的数据丢失(如果结果数字不适合某种类型int)。

int x = (int) Math.round(real_number)

例子:

陈述 结果
int x = (int) Math.round(4.1);
4
int x = (int) Math.round(4.5);
5
int x = (int) Math.round(4.9);
5

Math.ceil()方法

Math.ceil()方法将数字四舍五入整数。以下是示例:

陈述 结果
int x = (int) Math.ceil(4.1);
5
int x = (int) Math.ceil(4.5);
5
int x = (int) Math.ceil(4.9);
5

Math.floor()方法

Math.floor()方法将数字向下舍入为整数。以下是示例:

陈述 结果
int x = (int) Math.floor(4.1);
4
int x = (int) Math.floor(4.5);
4
int x = (int) Math.floor(4.9);
4

当然,当将数字向下舍入为整数时,简单地使用类型转换运算符会更容易:(int)

陈述 结果
int x = (int) 4.9
4

如果您觉得很难记住这些名字,简短的英语课会有所帮助:

  • Math意味着数学
  • Round表示圆形
  • Ceiling意味着天花板
  • Floor意味着地板

2. 浮点数的结构

该类型可以存储从到double范围内的值。这种巨大的值范围(与类型相比)是由于类型(以及)具有与整数类型完全不同的内部结构这一事实。在内部,该类型将其值编码为两个数字:第一个称为尾数第二个称为指数-1.7*10308+1.7*10308intdoublefloatdouble

假设我们有数字123456789并将其存储在一个double变量中。当我们这样做时,数字被转换为,并且在内部类型存储两个数字 —和。尾数(“数字的重要部分”或尾数)以红色突出显示,而指数以蓝色突出显示。1.23456789*108double234567898

这种方法可以存储非常大的数字和非常小的数字。但是由于数字的表示限制为 8 个字节(64 位),并且其中一些位用于存储指数以及尾数的符号和指数的符号),因此可用于表示尾数的最大位数15 岁

这是对实数结构的非常简化的描述。


3. 处理实数时精度损失

使用实数时,请始终牢记实数 并不精确。从十进制转换为二进制时,可能总是会出现舍入错误转换错误。此外,最常见的错误来源是在完全不同的尺度上添加/减去数字时的精度损失

最后一个事实对于新手程序员来说有点令人兴奋。

如果我们从 中减去,我们得到。1/109109109

在完全不同的尺度上减去数字 解释
 1000000000.000000000;
-         0.000000001;
 1000000000.000000000;
第二个数字非常小,这将导致其有效数字(以灰色突出显示)被忽略。15位有效数字以橙色突出显示。

我们能说什么,编程与数学不同。


4.比较实数时的陷阱

另一个危险在于等待程序员比较实数。它在处理实数时出现,因为舍入误差会累积。结果是,在某些情况下,预期实数相等,但事实并非如此。反之亦然:数字应该不同,但它们是相等的。

例子:

陈述 解释
double a = 1000000000.0;
double b = 0.000000001;
double c = a - b;
The value of the variable a will be1000000000.0
变量的值c 将是1000000000.0
(变量中的数字b 过小)

在上面的例子中,aandc不应该相等,但它们是。

或者我们再举一个例子:

陈述 解释
double a = 1.00000000000000001;
double b = 1.00000000000000002;
变量的值a 将是1.0
变量的值b 将是1.0

5. 一个有趣的事实strictfp

Java有一个特殊的关键字strictfpstrict floating point ),这是其他编程语言所没有的。你知道你为什么需要它吗?它会降低浮点数运算的准确性。这是它是如何形成的故事:

Java的创造者:
我们真的希望 Java 超级流行,并在尽可能多的设备上运行 Java 程序。所以我们确保 Java 机器的规范规定所有程序必须在所有类型的设备上以相同的方式运行!
英特尔处理器制造商:
嘿大家!我们改进了处理器,现在所有实数在我们的处理器中都使用 10 字节而不是 8 字节表示。更多字节意味着更多有效数字。这意味着什么?这是正确的!现在你的科学计算会更加准确!
科学家和所有参与超精密计算的人:
凉爽的!做得好。好消息!
Java的创造者:
不不不,你们!我们已经告诉过您,所有 Java 程序必须在所有设备上以相同的方式运行。我们将强制禁用在 Intel 处理器中使用 10 字节实数的功能。
现在一切都好了!不要感谢我们。
科学家和所有参与超精密计算的人:
你完全疯了吗?赶紧把一切恢复原样!
Java的创造者:
伙计们,这是为你们好!试想一下:所有 Java 程序在所有设备上的运行方式都相同。这太酷了!
科学家和所有参与超精密计算的人:
不,一点也不酷。赶紧把一切恢复原状!或者知道我们会将您的 Java 放在哪里?
Java的创造者:
唔。你为什么不马上说呢?当然,我们会把它放回去。
我们恢复了您使用最新处理器的所有功能的能力。
对了……我们还特地strictfp给语言加了关键词。如果你把它写在函数名之前,那么在所有设备上该函数内涉及实数的所有操作都同样糟糕