计算机组成原理 第3次作业


  1. 通过编程验证 **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 类型变量 df 只保留了小数点后的前 7 位有效数字,而 d 则保留了小数点后的前 15 到 16 位有效数字。

float 类型使用 32 位(4 字节)来存储浮点数,其中 1 位用于表示符号位,8 位用于表示指数,剩余的 23 位用于表示尾数。这意味着 float 类型最多可以表示 个不同的数,也就是大约 位的有效数字。因此,float 类型只能精确表示到第 7 位有效数字。

double 类型使用 64 位(8 字节)来存储浮点数,其中 1 位用于表示符号位,11 位用于表示指数,剩余的 52 位用于表示尾数。这意味着 double 类型最多可以表示 个不同的数,也就是大约 位的有效数字。因此,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(不是一个数字),表示计算无法完成。

  1. **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

浮点数在计算机内部以二进制形式表示时,会存在精度损失。当我们使用等于操作符(==)比较两个浮点数时,它会将它们的二进制表示形式进行比较。由于精度损失,这样的比较可能会产生不准确的结果。

  1. 类型转换和移位操作。

编程以实现以下各种操作:

  • 给定一个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倍。