- 通过编程验证
**float**
和**double**
类型的精度(即十进制有效位的位数),在实验报告中给出结果及解释;编程计算**-8.0/0**
、**sqrt(-4,0)**
的运算结果,并对结果给予解释。
static void testPrecision() {
float f = 123456789.123456789f;
double d = 123456789.123456789;
System.out.println("float f = " + f);
System.out.println("double d = " + d);
}
运行结果:
float f = 1.2345679E8
double d = 1.2345678912345679E8
在上述代码中,我们分别定义了一个 float
类型变量 f
和一个 double
类型变量 d
。f
只保留了小数点后的前 7 位有效数字,而 d
则保留了小数点后的前 15 到 16 位有效数字。
float
类型使用 32 位(4 字节)来存储浮点数,其中 1 位用于表示符号位,8 位用于表示指数,剩余的 23 位用于表示尾数。这意味着 float
类型最多可以表示 $ 2^{23} $ 个不同的数,也就是大约 $ 8 $ 位的有效数字。因此,float
类型只能精确表示到第 7 位有效数字。
double
类型使用 64 位(8 字节)来存储浮点数,其中 1 位用于表示符号位,11 位用于表示指数,剩余的 52 位用于表示尾数。这意味着 double
类型最多可以表示 $ 2^{52} $ 个不同的数,也就是大约 $ 16 $ 位的有效数字。因此,double
类型可以精确表示到第 15 到 16 位有效数字。
static void testCaculation() {
double result1 = -8.0 / 0;
double result2 = Math.sqrt(-4.0);
System.out.println("-8.0/0 = " + result1);
System.out.println("sqrt(-4,0) = " + result2);
}
运行结果:
-8.0/0 = -Infinity
sqrt(-4,0) = NaN
在计算机中,当执行除以 0 的操作时,根据 IEEE 754 浮点数标准,结果会被定义为正无穷大、负无穷大或 NaN(不是一个数字)中的一个。
当被除数为正数时,除以 0 的结果为正无穷大(+Infinity
)。当被除数为负数时,除以 0 的结果为负无穷大(-Infinity
)。当被除数为 0 时,除以 0 的结果为 NaN。
因此,对于表达式 -8.0/0
,被除数为负数 -8.0
,因此结果为负无穷大(-Infinity
)。
对于表达式 sqrt(-4.0)
,它是求负数的平方根,而在实数域中,负数的平方根是一个虚数,不能用实数来表示。在计算机中,sqrt()
函数不支持计算负数的平方根,会返回 NaN(不是一个数字),表示计算无法完成。
- 令
**float a = (100 + 1.0/3) - 100**
,**float b = 1.0 / 3**
;请回答逻辑表达式**a==b**
的取值是什么?如果变成**double a = (100 + 1.0/3) - 100**
,**double b = 1.0 / 3**
,**a==b**
的取值又是什么?通过程序计算和检验,对结果进行说明。
static void testEquality() {
float aFloat = (100 + 1.0f / 3) - 100;
float bFloat = 1.0f / 3;
double aDouble = (100 + 1.0 / 3) - 100;
double bDouble = 1.0 / 3;
System.out.println("float a == b: " + (aFloat == bFloat));
System.out.println("double a == b: " + (aDouble == bDouble));
}
运行结果:
float a == b: false
double a == b: false
浮点数在计算机内部以二进制形式表示时,会存在精度损失。当我们使用等于操作符(==)比较两个浮点数时,它会将它们的二进制表示形式进行比较。由于精度损失,这样的比较可能会产生不准确的结果。
- 类型转换和移位操作。
编程以实现以下各种操作:
- 给定一个short型数据-12345,分别转为int、unsigned short、unsigned int、float类型的数据
- 给定一个int型数据2147483647,分别转为short、unsigned short、unsigned int、float类型的数据
- 给定一个float型数据123456.789e5,转换成double型数据
- 给定一个double型数据123456.789e5,转换成float型数据
- 按short和unsigned short类型分别对-12345进行左移2位和右移2位操作
要求分别用十进制和十六进制形式打印输出以上各种操作的结果,并根据实验结果,回答下列问题:
- float型数据是否总能转换成等值的double型数据?
- 长数被截断成短数后可能发生什么现象?为什么?
- C语言中移位操作规则与操作对象的数据类型有关吗?
- 左移2位和右移2位操作分别相当于扩大和缩小几倍?
static void testTypeConversion1() {
short s = -12345;
int i = s;
int us = s & 0xffff;
long ui = s & 0xffffL;
float f = s;
System.out.println("int: " + i + " (0x" + Integer.toHexString(i) + ")");
System.out.println("unsigned short: " + us + " (0x" + Integer.toHexString(us) + ")");
System.out.println("unsigned int: " + ui + " (0x" + Long.toHexString(ui) + ")");
System.out.println("float: " + f + " (" + Float.toHexString(f) + ")");
}
static void testTypeConversion2() {
int i = 2147483647;
short s = (short) i;
int us = i & 0xffff;
long ui = i & 0xffffffffL;
float f = i;
System.out.println("short: " + s + " (0x" + Integer.toHexString(s) + ")");
System.out.println("unsigned short: " + us + " (0x" + Integer.toHexString(us) + ")");
System.out.println("unsigned int: " + ui + " (0x" + Long.toHexString(ui) + ")");
System.out.println("float: " + f + " (" + Float.toHexString(f) + ")");
}
static void testTypeConversion3() {
float f = 123456.789e5f;
double d = f;
System.out.println("double: " + d + " (" + Double.toHexString(d) + ")");
}
static void testTypeConversion4() {
double d = 123456.789e5;
float f = (float) d;
System.out.println("float: " + f + " (" + Float.toHexString(f) + ")");
}
static void testTypeConversion5() {
short s = -12345;
short s2 = (short) (s << 2);
int us = s & 0xffff;
int us2 = us << 2;
System.out.println("short: " + s2 + " (0x" + Integer.toHexString(s2) + ")");
System.out.println("unsigned short: " + us2 + " (0x" + Integer.toHexString(us2) + ")");
}
运行结果:
int: -12345 (0xffffcfc7)
unsigned short: 53191 (0xcfc7)
unsigned int: 53191 (0xcfc7)
float: -12345.0 (-0x1.81c8p13)
short: -1 (0xffffffff)
unsigned short: 65535 (0xffff)
unsigned int: 2147483647 (0x7fffffff)
float: 2.1474836E9 (0x1.0p31)
double: 1.2345678848E10 (0x1.6fee0ep33)
float: 1.2345679E10 (0x1.6fee0ep33)
short: 16156 (0x3f1c)
unsigned short: 212764 (0x33f1c)
由于没有指定语言,所以一开始用了Java,但是Java中没有unsigned的数据类型,只好手动模拟,于是又用C语言实现了一遍:
给定一个short型数据-12345,分别转为int、unsigned short、unsigned int、float类型的数据
#include <stdio.h>
int main() {
short s = -12345;
int i = s;
unsigned short us = s;
unsigned int ui = s;
float f = s;
printf("s = %d, %#x\n", s, s);
printf("i = %d, %#x\n", i, i);
printf("us = %u, %#x\n", us, us);
printf("ui = %u, %#x\n", ui, ui);
printf("f = %f\n", f);
return 0;
}
输出结果为:
s = -12345, 0xffffcfc7
i = -12345, 0xffffcfc7
us = 53191, 0xcfc7
ui = 4294954951, 0xffffcfc7
f = -12345.000000
给定一个int型数据2147483647,分别转为short、unsigned short、unsigned int、float类型的数据
#include <stdio.h>
int main() {
int i = 2147483647;
short s = i;
unsigned short us = i;
unsigned int ui = i;
float f = i;
printf("i = %d, %#x\n", i, i);
printf("s = %d, %#x\n", s, s);
printf("us = %u, %#x\n", us, us);
printf("ui = %u, %#x\n", ui, ui);
printf("f = %f\n", f);
return 0;
}
输出结果为:
i = 2147483647, 0x7fffffff
s = -1, 0xffffffff
us = 65535, 0xffff
ui = 2147483647, 0x7fffffff
f = 2147483648.000000
给定一个float型数据123456.789e5,转换成double型数据
#include <stdio.h>
int main() {
float f = 123456.789e5;
double d = f;
printf("f = %f, %a\n", f, f);
printf("d = %f, %a\n", d, d);
return 0;
}
输出结果为:
f = 12345678848.000000, 0x1.6fee0ep+33
d = 12345678848.000000, 0x1.6fee0ep+33
给定一个double型数据123456.789e5,转换成float型数据
#include <stdio.h>
int main() {
double d = 123456.789e5;
float f = d;
printf("d = %f, %a\n", d, d);
printf("f = %f, %a\n", f, f);
return 0;
}
输出结果为:
d = 12345678900.000000, 0x1.6fee0e1ap+33
f = 12345678848.000000, 0x1.6fee0ep+33
按short和unsigned short类型分别对-12345进行左移2位和右移2位操作
#include <stdio.h>
int main() {
short s = -12345;
unsigned short us = s;
printf("s = %d, %#x\n", s, s);
printf("s左移2位: %d, %#x\n", s << 2, s << 2);
printf("s右移2位: %d, %#x\n", s >> 2, s >> 2);
printf("us = %u, %#x\n", us, us);
printf("us左移2位: %u, %#x\n", us << 2, us << 2);
printf("us右移2位: %u, %#x\n", us >> 2, us >> 2);
return 0;
}
输出结果为:
s = -12345, 0xffffcfc7
s左移2位: -49380, 0xffff3f1c
s右移2位: -3087, 0xfffff3f1
us = 53191, 0xcfc7
us左移2位: 212764, 0x33f1c
us右移2位: 13297, 0x33f1
float型数据是否总能转换成等值的double型数据?
是的,float类型的数据可以转换为double类型的数据,但是可能会精度丢失。
长数被截断成短数后可能发生什么现象?为什么?
当一个长数被截断成短数后,如果截断的部分超过了短数所能表示的范围,就会发生数据溢出,即结果不再准确。这是因为长数和短数的字节数不一样,长数通常占用4个字节或8个字节,而短数通常只占用2个字节,因此短数能够表示的数值范围比长数小。
C语言中移位操作规则与操作对象的数据类型有关吗?
是的,C语言中移位操作的规则与操作对象的数据类型有关。对于有符号数,右移操作会保留符号位(即最高位),而左移操作不会。对于无符号数,左右移操作都不会保留符号位。另外,移位操作会将超出数据类型位数的位数截断。
左移2位和右移2位操作分别相当于扩大和缩小几倍?
左移n位相当于将数值乘以2的n次方,右移n位相当于将数值除以2的n次方。因此,左移2位相当于将数值扩大4倍,右移2位相当于将数值缩小4倍。