[英]Long Representation vs Double representation of positive and negative zero in java
我想知道不同数字类型中正零和负零之间的区别。
我了解 IEEE-754 用于浮点算术和双精度位表示,因此以下内容并不令人意外
double posz = 0.0;
double negz = -0.0;
System.out.println(Long.toBinaryString(Double.doubleToLongBits(posz)));
System.out.println(Long.toBinaryString(Double.doubleToLongBits(negz)));
// output
>>> 0
>>> 1000000000000000000000000000000000000000000000000000000000000000
令我惊讶的是,我对 java 中long
类型的位表示一无所知是,即使我右移(无符号>>>
),那么正零和负零的二进制表示是相同的
long posz = 0L;
long negz = -0L;
for (int i = 63; i >= 0; i--) {
System.out.print((posz >>> i) & 1);
}
System.out.println();
for (int i = 63; i >= 0; i--) {
System.out.print((negz >>> i) & 1);
}
// output
>>> 0000000000000000000000000000000000000000000000000000000000000000
>>> 0000000000000000000000000000000000000000000000000000000000000000
所以我想知道当我写以下内容时,java 从位表示中做了什么
long posz = 0L;
long negz = -0L;
编译器是否理解它们都是零并且忽略符号(因此将 0 分配给符号位)或者这里还有其他魔法吗?
还是这里有其他魔法?
是的。 2 的补码。
2 的补码有点神奇。 它实现了 2 个主要目标。 在开始讨论之前,让我们先讨论一下负零的概念。
负零实际上不是一回事。 问任何数学家“嘿,那么,负零是怎么回事?” 他们只会困惑地看着你。 这不是一回事。 在数学上,0 和 -0 完全一样。 不仅“几乎相同”,而且100%,完全,以所有可能的方式,相同。 我们通常不希望我们的数字能够同时代表5.0
和5.00
- 因为这两者是完全 100% 相同的。 如果您认为价值系统不应该浪费位试图区分5.0
和5.00
,那么希望能够将-0.0
和+0.0
表示为不同的实体同样奇怪。
所以,一开始就想要-0
有点奇怪。 所有的数字原语( long
、 int
、 short
、 byte
,我猜char
在技术上也是数字)都不能代表这个数字。 相反, long z = -0
归结为:
-
是一元运算符。就像2+5
使系统计算元素2和5的“加法”二元运算一样, -x
使系统计算“否定”的一元运算" 在元素x
上。对 0 应用否定运算会产生 0。这与写int x = 5 + 0;
没有什么不同。 +0
部分没有做任何事情。 -0
前面的-
没有做任何事情。与-0.0
相比,它确实做某事(让你负零, double
值,而不是正零)。z
中(因此,只有 0)。 没有办法判断那个减号是否存在。 它们都导致全零位,因此,计算机无法判断您是使用表达式-0
还是使用+0
初始化了该变量。 再次与double
where 你注意到有一点不同。
double
会有它呢?让我们稍微了解一下双打和 IEEE-754 数学的概念。
double
需要 64 位。 那么,从基本的纯数学原理来看,双精度数无法表示超过 2^64 个不同的可能值,您能够打破光速或使1+1=3
。
然而, double
旨在代表所有数字。 0 到 1 之间的数字比 2^64 个选项要多得多(实际上,0 到 1 之间存在无限数量的数字),而这只是 0 到 1。
所以,双打的实际工作方式是不同的。 从整个数字行中选择几个少于 2^64 的数字。 让我们称这些为有福的数字。
受祝福的人数分布不均。 您越接近1
,存在的祝福数字就越多。 换句话说,当您远离 1 时,两个受祝福的数字之间的距离会增加。例如,如果您从 1e100(一个 1 和一百个零)开始的 go 并想要找到下一个受祝福的数字,这是一种相当多的方法. 它实际上高于 1.0! - 1e100+1
实际上又是 1e100 ,因为double
数学的工作方式是,在你做它们的每一个最后的数学运算之后,最终结果会四舍五入到最接近的祝福数字。
让我们试试吧!
double d = 1e100;
System.out.println(d);
System.out.println(d + 1);
// prints: 1.0E100
// 1.0E100
但这意味着.. double
值实际上并不代表单个数字! . 任何给定的 double 所代表的实际上是这个概念:
一个未知数,其值介于 [D -, D + ] 之间,其中 D 是与此值表示的未知数接近的受祝福数,并且 是 D 与任一上最近的受祝福数之间距离的一半边。
鉴于它通常非常小,这“足够好”。 但是这种怪异确实解释了为什么如果准确性很重要(例如货币),您真的,真的不想要任何double
业务。永远不要将它们存储在双倍中!)
鉴于此, -0.0
代表什么? 实际上不仅仅是0。它具体表示:一个未知数,其值介于 [-, 0] 之间,其中 0 是实数零(并且 this,没有符号),并且是Double.MIN_VALUE
:可表示的最小非零正数带double
。
这就是-0.0
和+0.0
都存在的原因:它们实际上是不同的概念。 很少相关,但有时确实如此。 与例如long
相比,其中5
仅表示5
而不是“在 4.5 和 5.5 之间”,因为 long 从根本上不承认小数部分首先存在。 鉴于5
仅表示5
,那么0
仅表示0
,并且首先不存在负零之类的东西。
2 的补码是一个很酷的系统。 它有两个简洁的属性:
它对大于、小于和除数很重要。
2 的补码是这样工作的:要对一个数取反,取所有位并将它们翻转(即对位执行 NOT 操作)。 然后,加 1。
让我们试试吧!
int x = 5;
int y = -x;
for (int i = 31; i >= 0; i--) {
System.out.print((x >>> i) & 1);
}
System.out.println();
for (int i = 31; i >= 0; i--) {
System.out.print((y >>> i) & 1);
}
System.out.println();
// prints 00000000000000000000000000000101
// 11111111111111111111111111111011
正如我们所见,应用了“翻转所有位并加 1”算法。
2s 补码当然是可逆的:如果你连续两次“翻转所有位并加 1”,你会得到相同的数字。
现在让我们试试-0
。 0是32个0位,然后全部翻转,然后加1:
00000000000000000000000000000000
11111111111111111111111111111111 // flip all
100000000000000000000000000000000 // add 1
00000000000000000000000000000000 // that 1 fell off
并且因为 int 只能存储 32 位,所以最终的“1”会从末尾脱落。 我们又是零。
现在让我们用字节(稍微小一点)的 go 并尝试将 200 和 50 加在一起。
11001000 // 200 in binary
00110010 // 50 in binary
-------- +
11111010 // 250 in binary.
现在让我们改为 go:哦等等,哎呀,这是一个错误,实际上这些数字是 2s 补码。 那不是200,诺诺。 11001000 是一个位序列,实际上意味着(让我们应用“翻转所有位,加 1”方案:00111000 - 它实际上是 -56。所以该操作旨在表示“-56 + 50”。即 -6。-6二进制是(写出 6,翻转位,加 1):
00000110
11111001
11111010
嘿,现在看,没有任何变化,结果是一样的! 因此,当计算机执行x + y
时,其中 x 和 y 是数字,计算机不在乎。 无论 x 是“无符号数”还是“有符号的 2s 补数”,操作都是相同的。
这就是应用 2s 补码的原因。 它使数学更快。 CPU 不必费心处理符号位的分支问题。
从这个意义上说,在 java 中, int
、 long
、 char
、 byte
和short
既不是有符号的也不是无符号的更正确,它们只是。 至少对于 +、-、++ 和 -- 而言。 不, int
已签名的想法基本上是System.out.println(int)
的属性 - 该方法选择将位序列 11111111111111111111111111111111 呈现为“-1”而不是 4294967296。
long
没有负零这样的东西。 只有float
和double
具有不同的正零和负零表示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.