程序员学数据结构(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-19 10:47:05

点击下载

作者:(美)威廉·史密斯(William Smith)

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

程序员学数据结构

程序员学数据结构试读:

前言

作为软件开发人员,在面对全新的任务和挑战时,我们常常会将这些问题分解为自己所熟知的各类解决方案和代码片段,并根据客户需求和任务截止日期(或称为发薪日),选出最快的方案进行开发。但是,这样做只是单纯地完成了工作要求,有时对于学到更多的开发技巧和理念从而成为一名更优秀、更高效的开发者的帮助并没有想象中的那么大。

本书涵盖了数据类型和数据结构的相关知识,能够帮助编程新手、胸怀抱负的开发人员或者是有一定经验却疲于奔命的程序员理清上述领域的基础概念。为此,本书会从常用的数据类型和数据结构开始,对它们的创建方式、工作原理、功能实现以及日常应用的适用范围等话题展开详细的介绍。通过本书,读者不仅能掌握更多的基础知识、编程技巧和开发能力,还能学到新的开发理念,从而进一步利用好这些基本的数据结构。本书涵盖的内容

第1章“数据类型:基本的数据结构”概述了构成数据结构的基本数据类型。本章对基本数据类型做了快速回顾,某些读者甚至都已熟知了其中所讨论的部分内容。读者需要特别注意这些数据类型所适用的典型应用、最佳实现以及在不同开发平台上它们之间的区别。

第2章“数组:基本数据集”介绍了数组。本章将会对数组这种数据结构的具体细节、典型应用和它在不同开发语言中的区别展开详细讨论。本章是重要的基础性章节,后续讨论到的很多数据结构都是基于数组构建的。

第3章“列表:线性数据集”涵盖了列表数据结构的具体细节,包含列表的常用操作、典型应用以及它在不同开发语言中的区别。

第4章“栈:后入先出的数据集”介绍了栈这种数据结构。读者将会从本章学习到栈的具体细节,其中包括栈的常用操作、典型应用以及它在不同开发语言中的区别。

第5章“队列:先入先出的数据集”介绍了队列数据结构的具体细节,包括队列的常用操作、典型应用以及它在不同开发语言中的区别。

第6章“字典:关键字数据集”深入探讨了字典数据结构的具体细节,包括字典最常用的操作、典型应用以及它在不同开发语言中的区别。

第7章“集合:不包含重复项的数据集”讨论了集合数据结构的具体细节,其中包括集合论的基础知识、集合的常用操作、典型应用以及它在不同开发语言中的区别。

第8章“结构体:更为复杂的数据类型”探索了结构体的具体细节,包括结构体的常用操作、典型应用以及它在不同开发语言中的区别。

第9章“树:非线性数据结构”介绍了抽象树结构,尤其是二叉树的具体细节,其中包括树结构的常用操作、典型应用以及它在不同开发语言中的区别。

第10章“堆:有序树”深入探讨了堆数据结构的具体细节,包括堆的常用操作、典型应用以及它在不同开发语言中的区别。

第11章“图:互相连接的对象”介绍了图这种数据结构的具体细节,包括图的常用操作、典型应用和它在不同开发语言中的区别。

第12章“排序:为混乱带来秩序”是本书的高级章节,引出了排序的基本概念,重点介绍了一些常用的排序算法,其中还包括了这些排序算法的复杂度、典型应用以及它们在不同开发语言中的区别。

第13章“查找:找你所需”同样是本书的高级章节,引出了在特定数据结构上进行查找操作的概念,重点介绍了一些常用的查找算法,其中还包括了这些查找算法的复杂度、典型应用以及它们在不同开发语言中的区别。读者所需的准备工作

本书为使用Mac、PC甚至是Linux计算机的读者提供了丰富的代码示例。为了充分理解本书中的内容,读者需要具备一台现代计算机,以及一个在该计算机上正常运行的开发环境,如Visual Studio、XCode、Eclipse或NetBeans等,以便运行这些示例代码。本书的目标读者

本书能够帮助读者提高他们在数据结构相关领域的编程知识和技巧。具体来说,本书的目标读者是初学编程或自学编程的人员,以及那些编程经验不满4年的开发人员。本书主要通过移动应用开发中最常用的4种编程语言来对书中的内容进行讲解,因此目标读者还包含那些对移动应用开发感兴趣的编程人员。本书的读者应具有基本的编程概念,能够创建控制台应用程序,并且能使用相关的集成开发环境(IDE)。排版约定

本书的正文部分会根据内容使用不同的格式加以区分。以下是这些格式的示例和它们所代表的意义。

本书中的每章都会包含对应的案例学习或相似的代码示例,用以详细介绍特定数据结构的使用。因此,本书会含有很多示例代码。

示例代码段会以下列格式印刷:public boolean isEmpty(){ return this._commandStack.empty();}

需要重点关注的代码会用粗体进行标注:func canAddUser(user: EDSUser) -> Bool{ if (_users.contains(user)) { return false; } else { return true;}

新名词或关键词会以粗体印刷。可能出现在菜单或对话框中的语句会用以下字体表示,如isFull()等。

本书还会对算法涉及的数学概念进行讨论,会使用大O记号来标注所有的算法复杂度,如“然而,这只能算是一个很小的心理安慰,2因为整个选择排序算法的复杂度为O(n)”中所示。需要注意的内容将以这种格式呈现。提示和技巧将以这种格式呈现。读者反馈

我们欢迎您对本书的反馈。若需对本书提出任何意见,请将您的反馈用电子邮件发送至 feedback@packtpub.com,并在邮件标题中标明本书的书名。我们将根据您的反馈进行评估。若您希望成为一名作者,愿意在您精通的领域发表著作,可以访问Packt官网获得更多信息。客户支持

我们为每一位拥有Packt 图书的读者都提供了相应服务。下载示例代码

您可访问Packt官网下载本书所有的示例代码文件。若您在别的地方购买了本书,可访问Packt官网并进行注册,我们会通过电子邮件将这些示例代码文件发送给您。

您也可通过以下步骤下载本书的示例代码文件:

1.在我们的网站上使用电子邮件地址注册或登录您的账户;

2.将鼠标指针移动至网页顶端的SUPPORT标签上;

3.单击Code Downloads & Errata;

4.在Search框中输入本书的书名;

5.选出您所查找的书目;

6.从下拉菜单中选择您在何处购买到本书的;

7.单击Code Download。

下载到了文件后,请您确保拥有以下解压缩软件的最新版本,以便文件得到正确解压:● Windows版WinRAR / 7-Zip;● Mac版Zipeg / iZip / UnRarX;● Linux版7-Zip / PeaZip。

本书的示例代码文件也托管在GitHub中,您也可在异步社区(www.epubit.com)上下载。我们还在GitHub中托管了其他大量的图书和视频,欢迎查阅!勘误

我们使用了各种手段,尽可能地保证本书内容的正确性,但事无绝对,书中可能还存在未发现的错误。若您发现书中内容或代码存在错误之处,请及时向我们反馈,我们会非常感谢您的帮助。您可访问Packt官网选中出错的图书,单击Errata Submission Form链接,输入错误的详细内容,来向我们报告这些错误。我们会对您提交的内容进行核实,若属实,我们进行对应的勘误,并将该内容添加至对应图书的勘误表中。

若要查看之前提交的勘误内容,可访问Packt官网在搜索框中输入对应的书名进行查看,相应的内容会出现在Errata中。反盗版声明

互联网上的盗版问题是所有媒体都正面临的严峻问题。Packt公司对待版权保护和授权工作的态度非常严肃。若您在互联网上遇到了我公司所有内容的非法复制品,无论该复制品是以何种方式进行呈现,我们都希望您能立即向我们提供展示该复制品的网站地址和网站名称,以便我们进行补救。

可将涉嫌盗版的材料通过 copyright@packtpub.com发送给我们。

我们非常感谢您对作者和我们内容保护工作所提供的支持。疑问

若您对本书有任何疑问,可通过 questions@packtpub.com 与我们取得联系,我们将尽可能地帮助您解决问题。第1章 数据类型:基本的数据结构

将数据类型称作基本的数据结构可能有些用词不当,但开发人员往往使用这些数据类型来构建他们自己的类和数据集,因此从他们的角度思考的话,这样的称呼也未尝不可。所以,在我们进一步学习数据结构之前,最好先快速地回顾一下数据类型,毕竟这些数据类型是本书内容的基础。本章旨在从全局角度回顾那些最常用和最重要的基础数据类型。如果你已经对这些基础概念有了较深刻的理解,可视情况略读或跳过本章。

本章将涵盖以下主要内容:● 数值数据类型;● 类型转换、缩限转换及扩展转换;● 32位和64位架构数据类型的区别;● 布尔数据类型;● 逻辑运算;● 运算优先级;● 嵌套运算;● 短路求值;● 字符串数据类型;● 字符串的可变性。1.1 数值数据类型

C#、Java、Objective-C和Swift这4种语言中全部数值数据类型的详细说明都可以再写一本书了。这里,我们只回顾每种语言中最常用的数值数据类型标识符。评价这些数据类型最简单的方法是基于其实际数据大小用每个语言分别举例,并在同一个框架内来分析讨论。看起来一样,实际却不一样!当在多个移动平台上开发应用时,应当注意到不同的语言可能会共用同一个/套数据类型标识符或关键字,但从底层来看,这些标识符并不一定等价。同样地,同一种数据类型在不同的语言中也可能会有不同的标识符。以16位无符号整型(16-bit unsigned integer)为例,在Objective-C中它被称作unsigned short类型。但在C#或Swift里,却分别用ushort类型和UInt16类型来表示。Java规定16位无符号整型只能用作char类型,尽管这个类型通常不用于数值型数据。以上每一种数据类型都表示一个16位无符号整型,只是名称有所不同。这貌似问题不大,但当你分别使用每个平台的原生语言为多个设备进行应用开发时,为保证一致性,需注意到这种差别。否则,将会带来不同平台上出现特定错误/漏洞的风险,这将是非常难以检测和判断的。1.1.1 整型

整型数据类型的定义为表示有符号(负值、零或正值)或无符号(零或正值)的整数。对于整型,每种语言都有其特定的标识符和关键字,因此按照存储长度来思考最为简便。为达到我们的目的,我们只讨论表示8、16、32和64位存储对象的整型。

8位数据类型,或统称为字节(byte),是我们探讨的最小的数据8类型。如果你复习过二进制数学,你会知道1个8位的内存块可表示277或256个值。有符号字节的可取值范围为−128~127或−2~2−1。无8符号字节的可取值范围为0~255或0~2−1。

除了一些特殊情况,16位数据类型通常称为短整型(short)。这1615种数据类型可表示2个值。有符号短整型的可取值范围为−2~15162−1。无符号短整型的可取值范围为0~2−1。

32位数据类型一般被认为是整型,有些时候也会被认为是长整3231型(long)。整型可表示2个值。有符号整型的可取值范围为−2~31322−1。无符号整型的可取值范围为0~2−1。

最后,64位数据类型一般被认为是长整型,而Objective-C中规定64其为双长整型(long long)。长整型可以表示2个值。有符号长整型6363的可取值范围为−2~2−1。无符号长整型的可取值范围为0~642−1。需注意的是,以上所提到的取值在我们所使用的4种语言中是一致的,但在其他的语言中可能会有细微的变化。熟悉所用语言数值标识符的详细细节总是一个好主意,尤其是当你需要用到标识符规定极值的情况下。C#

C#用整型来表示整数类型。它提供byte和sbyte两种机制来生成8位整型。这两种整型都能表示256个值,无符号的字节可取值范围为0~255。有符号的字节对负值提供支持,因此取值范围为−128~127,具体代码如下。// C#sbyte minSbyte = -128;byte maxByte = 255;Console.WriteLine("minSbyte: {0}", minSbyte);Console.WriteLine("maxByte: {0}", maxByte);/* 输出结果 minSbyte: -128 maxByte: 255*/

有趣的是,对于更长位的标识符C#改变了其命名模式。它用u作无符号(unsigned)的前缀,而不是使用sbyte中s作为有符号(signed)的前缀。因此C#分别使用short,ushort;int,uint;long,ulong作为16位、32位以及64位的整型标识符,其代码实现如下。short minShort = -32768;ushort maxUShort = 65535;Console.WriteLine("minShort: {0}", minShort);Console.WriteLine("maxUShort: {0}", maxUShort);int minInt = -2147483648;uint maxUint = 4294967295;Console.WriteLine("minInt: {0}", minInt);Console.WriteLine("maxUint: {0}", maxUint);long minLong = -9223372036854775808;ulong maxUlong = 18446744073709551615;Console.WriteLine("minLong: {0}", minLong);Console.WriteLine("maxUlong: {0}", maxUlong);/* 输出结果 minShort: -32768 maxUShort: 65535 minInt: -2147483648 maxUint: 4294967295 minLong: -9223372036854775808 maxUlong: 18446744073709551615*/Java

Java将整型作为其原始数据类型的一部分。Java只提供一种建立8位类型的方式,即byte。这是一个有符号的数据类型,因此可表示−127~128的取值。Java还提供了名为Byte的包装类,其不仅包装了原始值,并对像“42”这些能够转换为数值的可解析字符串或文本提供了额外的构造函数支持。这种模式重复体现在16位、32位和64位类型中。//Javabyte myByte = -128;byte bigByte = 127;Byte minByte = new Byte(myByte);Byte maxByte = new Byte("128");System.out.println(minByte);System.out.println(bigByte);System.out.println(maxByte);/* 输出结果 -128 127 127*/

Java和C#共用了所有整型的标识符,这意味着Java对8位、16位、32位和64位类型也提供了byte、short、int及long标识符。Java中只有一个例外,即提供了16位无符号数据类型的标识符char。值得注意的是,char类型通常只用作分配ASCII字符,而不是实际的整数数值。//Short ClassShort minShort = new Short(myShort);Short maxShort = new Short("32767");System.out.println(minShort);System.out.println(bigShort);System.out.println(maxShort);int myInt = -2147483648;int bigInt = 2147483647;//Integer classInteger minInt = new Integer(myInt);Integer maxInt = new Integer("2147483647");System.out.println(minInt);System.out.println(bigInt);System.out.println(maxInt);long myLong = -9223372036854775808L;long bigLong = 9223372036854775807L;//Long classLong minLong = new Long(myLong);Long maxLong = new Long("9223372036854775807");System.out.println(minLong);System.out.println(bigLong);System.out.println(maxLong);/* 输出结果 -32768 32767 32767 -2147483648 2147483647 2147483647 -9223372036854775808 9223372036854775807 9223372036854775807*/

在以上的代码中,须注意int类型和Integer类。不同于其他原始包装类,Integer并不和其支持的标识符共用名称。

此外,注意long类型和其分配的数值。在每个例子中,这些值都有后缀L。这是Java对long类型的要求,因为编译器将所有的数值文字默认翻译为32位整数。当需要明确说明字面数值是长于32位时,必须为其加上后缀L。不然的话,编译器可能会报错。然而,当给Long类型构造函数传递字符串值时,则不受这种限制:Long maxLong = new Long("9223372036854775807");Objective-C

对于8位数据,Objective-C提供了有符号和无符号两种格式的char类型。与其他语言相同,有符号的数据类型取值为−127~128,而无符号的类型取值为0~255。开发人员还可以选择使用Objective-C的定宽整型int8_t和uint8_t。这种模式重复体现在16位、32位和64位类型中。最后,Objective-C还以NSNumber类的形式对每种整型提供了面向对象的包装类。char或其他整型和其定宽整型有非常显著的区别。除了char类型总是为1字节长度以外,其他Objective-C中的整型长度会根据实现方式和底层架构的不同而改变。这是因为Objective-C是基于C语言的,而C语言被设计成能够在不同种类的底层架构上高效工作。尽管可以在运行和编译时就确定整型数据结构的确切长度,但在一般情况下,你只能确定的是short <= int <= long <= long long。这时定宽整型就派上用场了。在需要严格控制字节长度的情况下,(u)int_t整型可以让你精确定义出8位、16位、32位或64位长度的整数。//Objective-Cchar number = -127;unsigned char uNumber = 255;NSLog(@"Signed char number: %hhd", number);NSLog(@"Unsigned char uNumber: %hhu", uNumber);//固定宽度int8_t fixedNumber8 = -127;uint8_t fixedUNumber8 = 255;NSLog(@"fixedNumber8: %hhd", fixedNumber8);NSLog(@"fixedUNumber8: %hhu", fixedUNumber8);NSNumber *charNumber = [NSNumber numberWithChar:number];NSLog(@"Char charNumber: %@", [charNumber stringValue]);/* 输出结果 Signed char number: -127 Unsigned char uNumber: 255 fixedNumber8: -127 fixedUNumber8: 255 Char charNumber: -127*/

从上面的例子可以看出,当在代码中使用char类型时,必须指定标识符unsigned,例如unsigned char。signed是char类型的默认标识符,可以省略,这也意味着char类型与signed char等价。Objective-C中的其他整型也遵循这种模式。

Objective-C中更大的整型包括用于16位的short类型,用于32位的int类型以及用于64位的long long类型。以上每种整型都依照(u)int_t的模式有其定宽整型。NSNumber对每种整型都提供了支持方法。//更大的Objective-C整型short aShort = -32768;unsigned short anUnsignedShort = 65535;NSLog(@"Signed short aShort: %hd", aShort);NSLog(@"Unsigned short anUnsignedShort: %hu", anUnsignedShort);int16_t fixedNumber16 = -32768;uint16_t fixedUNumber16 = 65535;NSLog(@"fixedNumber16: %hd", fixedNumber16);NSLog(@"fixedUNumber16: %hu", fixedUNumber16);NSNumber *shortNumber = [NSNumber numberWithShort:aShort];NSLog(@"Short shortNumber: %@", [shortNumber stringValue]);int anInt = -2147483648;unsigned int anUnsignedInt = 4294967295;NSLog(@"Signed Int anInt: %d", anInt);NSLog(@"Unsigned Int anUnsignedInt: %u", anUnsignedInt);int32_t fixedNumber32 = -2147483648;uint32_t fixedUNumber32 = 4294967295;NSLog(@"fixedNumber32: %d", fixedNumber32);NSLog(@"fixedUNumber32: %u", fixedUNumber32);NSNumber *intNumber = [NSNumber numberWithInt:anInt];NSLog(@"Int intNumber: %@", [intNumber stringValue]);long long aLongLong = -9223372036854775808;unsigned long long anUnsignedLongLong = 18446744073709551615;NSLog(@"Signed long long aLongLong: %lld", aLongLong);NSLog(@"Unsigned long long anUnsignedLongLong: %llu", anUnsignedLongLong);int64_t fixedNumber64 = -9223372036854775808;uint64_t fixedUNumber64 = 18446744073709551615;NSLog(@"fixedNumber64: %lld", fixedNumber64);NSLog(@"fixedUNumber64: %llu", fixedUNumber64);NSNumber *longlongNumber = [NSNumber numberWithLongLong:aLongLong];NSLog(@"Long long longlongNumber: %@", [longlongNumber stringValue]);/* 输出结果 Signed short aShort: -32768 Unsigned short anUnsignedShort: 65535 fixedNumber16: -32768 fixedUNumber16: 65535 Short shortNumber: -32768 Signed Int anInt: -2147483648 Unsigned Int anUnsignedInt: 4294967295 fixedNumber32: -2147483648 fixedUNumber32: 4294967295 Int intNumber: -2147483648 Signed long long aLongLong: -9223372036854775808 Unsigned long long anUnsignedLongLong: 18446744073709551615 fixedNumber64: -9223372036854775808 fixedUNumber64: 18446744073709551615 Long long longlongNumber: -9223372036854775808*/Swift

Swift语言和其他语言类似,对于有符号和无符号的整数提供了各自的标识符,如Int8和UInt8。依据标识符名称来确定可应用的数据类型,这样的方式适用于Swift的每种整型,也使得Swift也许会成为最简单的语言。//Swiftvar int8 : Int8 = -127var uint8 : UInt8 = 255print("int8: \(int8)")print("uint8: \(uint8)")/* 输出结果 int8: -127 uint8: 255*/

为清晰地展示声明过程,上面的例子使用:Int8和:UInt8标识符明确地声明了数据类型。在Swift里,还可以不加这些标识符,让Swift在运行时动态地推断出数据类型。//更大的Swift整型var int16 : Int16 = -32768var uint16 : UInt16 = 65535print("int16: \(int16)")print("uint16: \(uint16)")var int32 : Int32 = -2147483648var uint32 : UInt32 = 4294967295print("int32: \(int32)")print("uint32: \(uint32)")var int64 : Int64 = -9223372036854775808var uint64 : UInt64 = 18446744073709551615print("int64: \(int64)")print("uint64: \(uint64)")/* 输出结果 int16: -32768 uint16: 65535 int32: -2147483648 uint32: 4294967295 int64: -9223372036854775808 uint64: 18446744073709551615*/

我为什么需要知道这些?你可能会问,我为什么需要知道数据类型的这些复杂细节?我难道不能只声明一个int对象或其他类似的东西后去写一些有趣的代码?现代计算机甚至是移动设备都能够提供近乎无穷的资源,所以这没什么大不了的,对吧?

事实并非如此。在你日常编程的经历中,大多数情况下随便使用哪一个数据类型可能都行。比如,遍历出任意一天西佛吉尼亚州全州车管部门签发的牌照列表,结果可能从几十个到上百个。你可以使用一个短整型变量或一个双长整型变量来控制for循环迭代。无论选用何种方式,这个循环为你的系统性能所带来的影响几乎可以忽略。

假设你在处理一组数据,这组数据中的每个离散结果都与16位类型匹配,而你习惯性地使用32位类型来处理,这会导致什么结果呢?这样做的结果会使处理这个数据集所需的内存空间翻倍。当离散结果只有100个或100 000个的时候,这样做可能并没什么不妥。但如果要处理的数据集很大,有百万个以及更多的离散结果的时候,这么做肯定会给系统性能带来非常大的影响。1.1.2 单精度浮点类型

单精度浮点(single precision floating point)类型通常称为单精度类型(float),用32位浮点容器能够存储比整型更高精度的数值,通常有6~7位有效数字。多种语言使用float关键字/标识符来标记单精度浮点数值,本书所讨论的4种语言也是如此。

需要注意的是,由于浮点数值不能精确地表示以10为基的数字,因此其精度受限于归零误差。浮点类型的数值算法非常复杂,无论何时其中的细节都与大部分开发人员不太相关。然而,学习浮点类型可以加深对底层技术及每种语言实现细节的了解。由于我并不是这方面的专家,因此只简单了解一下这些类型背后的科学原理,并不涉及具体的数学算法。我在本章末尾的附加资料中列出了这个领域专家们的一些研究成果,强烈建议你们学习。C#

C#使用float关键字标识32位浮点值。C#中float类型精度为6位有3838效数字,近似取值范围从−3.4×10~+3.4×10://C#float piFloat = 3.14159265358979323846264338327f;Console.WriteLine("piFloat: {0}", piFloat);/* 输出结果 piFloat: 3.141593*/

从上面的代码可以看出,使用float在赋值时有f作为后缀。这是因为C#和其他基于C的语言一样,在处理赋值语句右边的小数数字时,默认其为双精度型(double,稍后讨论)。如果在赋值时不用f或F后缀,而直接将一个双精度浮点的数值赋给单精度浮点类型,则会产生编译错误。

此外,注意到最后一位的归零误差。我们将30位有效数字的圆周率赋值给piFloat。但由于float只能保留6位有效数字,其后数字都会被约去。若直接对圆周率值保留6位有效数字,我们得到3.141592,但由于归零误差,浮点数的实际值为3.141593。Java

与C#相同,Java使用float标识符确定浮点值。Java中float类型精3838度为6或7位有效数字,近似取值范围为−3.4×10~+3.4×10://Javafloat piFloat = 3.141592653589793238462643383279f;System.out.println(piFloat);/* 输出结果 3.1415927*/

从上面的代码可以看出,浮点赋值操作有f后缀。这是因为Java和其他基于C的语言一样,在处理赋值语句右边的小数数字时,默认其为双精度型。如果在赋值时不加入f或F后缀,而直接将一个双精度浮点的数值赋给单精度浮点类型,则会产生编译错误。Objective-C

Objective-C使用float标识符确定浮点值。在Objective-C中,float3838类型精度为6位有效数字,近似取值范围从−3.4×10~+3.4×10://Objective-Cfloat piFloat = 3.14159265358979323846264338327f;NSLog(@"piFloat: %f", piFloat);NSNumber *floatNumber = [NSNumber numberWithFloat:piFloat];NSLog(@"floatNumber: %@", [floatNumber stringValue]);/* 输出结果 piFloat: 3.141593 floatNumber: 3.141593*/

从上面的代码可以看出,浮点赋值操作有f后缀。这是因为Objective-C和其他基于C的语言一样,在处理赋值语句右边的小数数字时,默认其为双精度型。如果在赋值时不加入f或F后缀,而直接将一个双精度浮点的数值赋给单精度浮点类型,则会产生编译错误。

此外,注意到最后一位的归零误差。我们将30位有效数字的圆周率赋值给piFloat。但由于float只能保留6位有效数字,其后数字都会被约去。若直接对圆周率值保留6位有效数字,我们得到3.141592,但由于归零误差,浮点数的实际值为3.141593。Swift

Swift使用float标识符确定浮点值。在Swift中,float类型精度为63838位有效数字,近似取值范围从−3.4×10~+3.4×10://Swiftvar floatValue : Float = 3.141592653589793238462643383279print("floatValue: \(floatValue)")/* 输出结果 floatValue: 3.141593*/

从上面的代码可以看出,浮点赋值操作有f后缀。这是因为Swift和其他基于C的语言一样,在处理赋值语句右边的实数数字时,默认其为双精度型。如果在赋值时不加入f或F后缀,而直接将一个双精度浮点的数值赋给单精度浮点类型,则会产生编译错误。

此外,注意到最后一位的归零误差。我们将30位有效数字的圆周率赋值给floatValue。但由于float只能保留6位有效数字,其后数字都会被约去。若直接对圆周率值保留6位有效数字,我们得到3.141 592,但由于归零误差,浮点数的实际值为3.141 593。1.1.3 双精度浮点类型

双精度浮点(double precision floating point)类型通常称为双精度型(double)。用64位浮点容器能够存储比整型更高精度的数值,该类型通常有15位有效数字。多种语言使用double关键字/标识符来标记双精度浮点数值,我们所讨论的4种语言也是如此。在大多数情况下,无论选用float还是double都无关紧要,除非内存空间较为紧张,这时应该尽可能选择float。很多人认为在多数情况下float比double更高效,一般来说,也确实是这样。但在一些情况下,double会比float更高效。事实上,由于存在太多无法在这里详述的标准,每种类型的效率会因情况而异。因此,如果需要在特定应用中达到最高的效率,你需要仔细研究各种影响因素来选用最合适的类型。如果对效率并不是那么在意,那就任选一个合适的类型,接着干活。C#

C#使用double关键字标识64位浮点数值。在C#中,double类型−324的精度为14或15位有效数字,近似取值范围从±5.0×10~±1.7×30810://C#double piDouble = 3.14159265358979323846264338327;double wholeDouble = 3d;Console.WriteLine("piDouble: {0}", piDouble);Console.WriteLine("wholeDouble: {0}", wholeDouble);/* 输出结果 piDouble: 3.14159265358979 wholeDouble: 3*/

从上面的代码可以看出,wholeDouble变量的赋值操作加了d后缀。这是因为C#和其他基于C的语言一样,在处理赋值语句右边的整数数字时,默认其为整型。如果在赋值时不加入d或D后缀,而试图直接将一个整型数值赋给双精度型,则会收到编译错误。

此外,注意到最后一位的归零误差。我们将30位有效数字的圆周率赋值给piDouble。但double只能保留14位有效数字,其后数字都会被约去。若直接对圆周率值保留15位有效数字,我们得到3.141 592 653 589 793,但由于归零误差,浮点数的实际值为3.141 592 653 589 79。Java

Java使用double关键字标识64位浮点数值。在Java中,double类−324型的精度为15或16位有效数字,近似取值范围为±4.9×10~±1.8×30810:double piDouble = 3.141592653589793238462643383279;System.out.println(piDouble);/* 输出结果 3.141592653589793*/

查看上面的代码,注意到最后一位的归零误差。我们将30位有效数字的圆周率赋值给piDouble。但double只能保留15位有效数字,其后数字都会被约去。若直接对圆周率值保留15位有效数字,我们得到3.141 592 653 589 793 2,但由于归零误差,浮点数的实际值为3.141 592 653 589 793。Objective-C

Objective-C也使用double标识符来确定64位浮点数值。在Objective-C中,double类型的精度为15位有效数字,近似取值范围从−3083082.3×10到1.7×10。为进一步提高精确性,Objective-C还提供了一个更高精度版本的double类型,即长双精度型(long double)。long double类型能够存储80位浮点数值,精度为19位有效数字,近似−49324932取值范围从3.4×10~1.1×10://Objective-Cdouble piDouble = 3.14159265358979323846264338327;NSLog(@"piDouble: %.15f", piDouble);NSNumber *doubleNumber = [NSNumber numberWithDouble:piDouble];NSLog(@"doubleNumber: %@", [doubleNumber stringValue]);/* 输出结果 piDouble: 3.141592653589793 doubleNumber: 3.141592653589793*/

查看上面的代码,注意到最后一位的归零误差。我们将30位有效数字的圆周率赋值给piDouble。但double只能保留15位有效数字,其后数字都会被约去。若直接对圆周率值保留15位有效数字,我们得到3.141 592 653 589 793 2,但由于归零误差,浮点数的实际值为3.141 592 653 589 793。Swift

Swift使用double标识符来确定64位浮点数值。在Swift中,double−308308类型的精度为15位有效数字,近似取值范围从2.3×10~1.7×10。需注意的是,根据Apple公司的Swift文档,当float和double类型均能满足需求时,推荐使用double类型://Swiftvar doubleValue : Double = 3.141592653589793238462643383279print("doubleValue: \(doubleValue)")/* 输出结果 doubleValue: 3.14159265358979*/

查看上面的代码,注意最后一位的归零误差。我们将30位有效数字的圆周率赋值给doubleValue。但由于double只能保留15位有效数字,其后数字都会被约去。若直接对圆周率值保留15位有效数字,我们得到3.141 592 653 589 793,但由于归零误差,浮点数的实际值为3.141 592 653 589 79。1.1.4 货币类型

由于浮点运算事实上是基于二进制数学的,有着固有的不准确性,因此单精度和双精度浮点类型无法精确地表示我们使用的十进制货币。乍一看,将货币用单精度或双精度浮点类型表示或许是个好主意,因为它能够约去运算过程带来的细微误差。但是,当把这些本来就不怎么精确的结果再进行大量、复杂的运算后,误差会不断累积,造成严重的偏差和难以跟踪的错误。这使得单精度和双精度浮点类型无法用于对精确度要求近乎完美的十进制货币。幸运的是,对于货币和其他需要进行高精度十进制运算的数学问题,我们所讨论的这4种语言都提供了相应机制。C#

C#使用decimal关键字来标识精确浮点值。在C#中,decimal的精−2828度为28或29位有效数字,取值范围为±1.0×10~±7.9×10: var decimalValue =NSDecimalNumber.init(string:"3.141592653589793238462643383279") print("decimalValue \(decimalValue)") /* 输出结果 piDecimal: 3.1415926535897932384626433833 */

上述代码中,我们将30位有效数字的圆周率赋值给decimal Value,但它只保留了28位有效数字。Java

Java以BigDecimal类的形式对货币类问题提供了一种面向对象的方案: BigDecimal piDecimal = new BigDecimal("3.141592653589793238462643383279"); System.out.println(piDecimal); /* 输出结果 3.141592653589793238462643383279 */

在上述代码中,我们把十进制值以文本形式作为构造函数的参数来初始化BigDecimal类。程序运行结果表明BigDecimal类返回了30位有效数字,没有精度损失。Objective-C

Objective-C以NSDecimalNumber类的形式对货币类问题提供了一种面向对象的方案: //Objective-C NSDecimalNumber *piDecimalNumber = [[NSDecimalNumber alloc]initWithDouble:3.14159265358979323846264338327]; NSLog(@"piDecimalNumber: %@", [piDecimalNumber stringValue]); /* 输出结果 piDecimalNumber: 3.141592653589793792 */Swift

Swift用与Objective-C中同名的类NSDecimalNumber对货币类问题提供了一种面向对象的方案。这个类在Swift和Objective-C中的初始化操作有些区别,但功能并无二致。var decimalValue =NSDecimalNumber.init(string:"3.141592653589793238462643383279")print("decimalValue \(decimalValue)")/* 输出结果 decimalValue 3.141592653589793238462643383279*/

注意,Objective-C和Swift两例的输出结果都有30位有效数字,这说明NSDecimal Number类适用于处理货币及其他十进制数值。透露一下,对于这些定制类型,还有一种简单的、可以说是更为优雅的替代方法。可使用int或long类型来进行货币计算,用分代替元来计数://C# long total = 316;//$3.161.1.5 类型转换

在计算机科学领域中,类型转换(type conversion或typecasting)是指将对象或数据从一种类型转换到另一种类型。例如,你调用了一个返回类型为整型的方法,并需要将这个返回值作为另一个方法的传入参数,但第二个方法要求传入参数必须是长整型。由于整型数值在定义上存在于长整型所允许的数值范围之内,因此int值可以重定义为long类型。

通常,可通过隐式转换(implicit conversion)或显式转换(也叫强制类型转换, explicit conversion)进行类型转换。此外,我们还需要了解静态类型语言(static languages)和动态类型语言(dynamic languages)的区别,才能完全领会类型转换的意义。1.静态类型语言和动态类型语言

静态类型语言会在编译时进行类型检查(type checking)。这意味着当你试图生成解决方案时,编译器会检查和实施程序中所有数据类型的约束条件。如果检查失败,会停止生成并报错。C#、Java以及Swift均是静态类型语言。

另一方面,动态类型语言会在执行时进行大多数甚至所有的类型检查。这意味着如果开发人员在编程时有所疏忽,程序或许在生成阶段一切正常,但在执行时可能会出错。Objective-C混用了静态和动态类型对象,它是一种动态类型语言。本章之前所讨论的用于存储数值型数据的纯C对象均为静态类型,而Objective-C中的NSNumber和NSDecimalNumber类均为动态类型。思考下面的Objective-C代码示例:double myDouble = @"chicken";NSNumber *myNumber = @"salad";

编译器会对第一行代码报错,内容为“Initializing 'double' with an expression of incompatible type 'NSString *'”。这是因为double是一个纯C的静态类型。编译器甚至在生成之前就知道应该怎样处理这个静态类型,因此这段代码通不过检查。

然而,对于第二行代码,编译器只会发出内容为“Incompatible pointer types initializing 'NSNumber *' with an expression of type'NSString *'”的警告。这是因为Objective-C的NSNumber类是一个动

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载