Go语言核心技术(卷一)之2.2-浮点数

Go提供了两种size的浮点数,float32和float64。它们的算术规范是由IEEE754国际标准定义,现代CPU都实现了这个规范。

浮点数能够表示的范围可以从很小到很巨大,浮点数的取值范围可以在math包中获取,math.MaxFloat32表示float32的最大值,大约是3.4e38,math.MaxFloat64大概是1.8e308。两个类型最小的非负值大概是1.4e-45和4.9e-324。

float32大约可以提供小数点后6位的精度,作为对比,float64可以提供小数点后15位的精度。通常应该优先选择float64,因此float32的精确度较低,在累积计算时误差扩散很快,而且float32能精确表达的最小正整数并不大,因为浮点数和整数的底层解释方式完全不同,具体见IEEE754详解

var f float32 = 16777216 // 1 << 24
fmt.Println(f == f+1)    // "true"!

浮点数字面量可以使用十进制数字表示:

const e = 2.71828 // (approximately)

小数点前面或者后面的数字都可以省略,例如:.707 , 1.   。对于那种很小或者很大的数值最好用科学计数法,在指数前加上e或者E:

const Avogadro = 6.02214129e23  // 阿伏伽德罗常数
const Planck   = 6.62606957e-34 // 普朗克常数

fmt打印浮点数时,若使用%g参数,会采用更高的精度更紧凑的表现形式进行打印,但是在打印表格数据时,%e(指数)或者%f(非指数的)的形式可能更合适,上面三个参数都可以控制打印的宽度和精度:

for x := 0; x < 8; x++ {
    fmt.Printf("x = %d e^x = %8.3f\n", x, math.Exp(float64(x)))
}

上面的代码使用了小数点后3位的精度打印了e的指数,然后x的打印宽度是8个字符:

x = 0   ex =    1.000
x = 1   ex =    2.718
x = 2   ex =    7.389
x = 3   ex =   20.086
x = 4   ex =   54.598
x = 5   ex =  148.413
x = 6   ex =  403.429
x = 7   ex = 1096.633

math包不仅包含了大量的数学函数,还包含了IEEE754规范下特殊浮点数的创建和查看:正无穷,表明数字太大溢出的情况;负无穷,被0除的结果;NaN(不是一个数值),用来表示无效运算的结果,0 / 0, math.Sqrt(-1)。

var z float64
fmt.Println(z, -z, 1/z, -1/z, z/z) //  "0 -0 +Inf -Inf NaN"

函数math.IsNaN测试一个数值是否是NaN,math.NaN会返回一个NaN值。虽然可以在数值计算中用NaN做为一个哨兵值,但是测试一个计算的结果是否等于NaN是很危险的,因为任何值跟NaN比较的结果都是false:

nan := math.NaN()
fmt.Println(nan == nan, nan < nan, nan > nan) // "false false false"

如果一个返回浮点数的函数可能失败,那最好还是单独的报告失败:

func compute() (value float64, ok bool) {
    // ...
    if failed {
        return 0, false
    }
    return result, true
}

下面的程序演示了通过浮点数计算来生成图形,使用了z = f(x,y)来进行三维建模,使用了SVG格式做图像输出,SVG是一个用于绘制矢量线的XML标准。下图战士了sin(r)/r函数生成的图形,r = sqrt(x*x + y*y):

// Surface computes an SVG rendering of a 3-D surface function.
package main

import (
    "fmt"
    "math"
)

const (
    width, height = 600, 320            // canvas size in pixels
    cells         = 100                 // number of grid cells
    xyrange       = 30.0                // axis ranges (-xyrange..+xyrange)
    xyscale       = width / 2 / xyrange // pixels per x or y unit
    zscale        = height * 0.4        // pixels per z unit
    angle         = math.Pi / 6         // angle of x, y axes (=30°)
)

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)

func main() {
    fmt.Printf("<svg xmlns=‘http://www.w3.org/2000/svg‘ "+
        "style=‘stroke: grey; fill: white; stroke-width: 0.7‘ "+
        "width=‘%d‘ height=‘%d‘>", width, height)
    for i := 0; i < cells; i++ {
        for j := 0; j < cells; j++ {
            ax, ay := corner(i+1, j)
            bx, by := corner(i, j)
            cx, cy := corner(i, j+1)
            dx, dy := corner(i+1, j+1)
            fmt.Printf("<polygon points=‘%g,%g %g,%g %g,%g %g,%g‘/>\n",
                ax, ay, bx, by, cx, cy, dx, dy)
        }
    }
    fmt.Println("</svg>")
}

func corner(i, j int) (float64, float64) {
    // Find point (x,y) at corner of cell (i,j).
    x := xyrange * (float64(i)/cells - 0.5)
    y := xyrange * (float64(j)/cells - 0.5)

    // Compute surface height z.
    z := f(x, y)

    // Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).
    sx := width/2 + (x-y)*cos30*xyscale
    sy := height/2 + (x+y)*sin30*xyscale - z*zscale
    return sx, sy
}

func f(x, y float64) float64 {
    r := math.Hypot(x, y) // distance from (0,0)
    return math.Sin(r) / r
}

这里的corner函数返回两个值,分别是网格顶点的x,y坐标。
如果要深入解释图像生成的原理,这里需要一些几何学知识。但是这里我们跳过几何学原理,毕竟这个程序主要是为了演示浮点数的运算。程序本质上是三个坐标系间的映射,如下图所示,第一个是100*100的二维网格,每个格子都有坐标(i,j),从坐标系原点(0,0)开始延伸。绘制时是从远处开始绘制,因此远处先绘制的多边形可能被后绘制的多边形覆盖。

第二个坐标系是三维网格组成的,坐标(x,y,z),其中x和y是i和j的线性函数,通过平移转换把原点变为中心点,然后通过xyrange进行缩放。高度z是f(x,y)的值。

第三个坐标系是一个二维的画布,起点(0,0)在左上角。画布中心坐标(sx,sy),我们使用等角投影将三维点(x,y,z)投影到二维的画布中。画布上的点离右边越远,x和y值越大,z值越小。x和y的垂直缩放系数是30度角的sin值,水平缩放系统是30度角的cos值。z的缩放系数0.4是一个任意的值。

对于二维网格中的每一个网格单元,main函数会计算该单元在画布上对应的多边形ABCD的顶点,B对应顶点(i,j),A、C、D是B的邻接点,然后输出SVG的绘制指令。

时间: 2024-10-21 13:37:10

Go语言核心技术(卷一)之2.2-浮点数的相关文章

Go语言核心技术(卷一)之2.1-整数

第二章 序 在计算机底层,一切都是比特位.然而计算机一般都操作固定大小的数,称之为字(word).字会被解释为整数.浮点数.比特位数组.内存地址等,这些字又可以进一步聚合成数据包(packet).像素点.作品集.是个或者其它任何对象.Go语言提供了多样化的数据组织方式,这些数据类型能提供硬件层面的兼容性,也能让程序员方便的组合成更复杂的数据类型. Go语言的数据类型分为四大类:基本类型,复合类型,引用类型及接口类型.本章将介绍基本类型:数字,字符串,布尔值. 一.整数 Go语言的数值数据类型包括

Go语言核心技术(卷一)之2.4-布尔值

布尔值有两种:true和false.if和for语句中的条件都是布尔值,比较运算符== .<等也会产生一个布尔值.一元运算符! 是逻辑取反操作,所以!true就是false.Go语言提倡简洁的风格,因此我们会将x == true这种表达式直接写成x:if x {..} . 布尔值可以结合&&(AND) .|| (OR)一起使用,这两个操作符都有短路特性,如果操作符的左边表达式就能决定结果了,那操作符右边的表达式不会求值: s != "" && s[

java核心技术卷一

java核心技术卷一 java基础类型 整型 数据类型 字节数 取值范围 int 4 +_2^4*8-1 short 2 +_2^2*8-1 long 8 +_2^8*8-1 byte 1 -128-127       浮点类型 数据类型 字节数 取值范围 小数位数 float 4 10^-38~10^38和-10^-38~-10^38 小数位数6-7 double 4 10^-308~10^308和-10^-308~-10^308 15位小数         boolean 类型和char 类

C语言核心技术-变量和数据类型

C语言核心技术-变量和数据类型 C语言核心技术 2.1 计算机的计算单位 2.1.1 容量单位 2.1.2 速度单位 2.2 数据类型 2.2.1 数据为什么要分类 2.2.2 C语言常用基本数据类型 2.2.3 sizof关键字查看数据类型占据的内存容量 2.2.4 数据的有符号和无符号 2.2.5 数据类型的极限 2.3 常量 2.4 变量 2.4.1 变量的本质 2.4.2 变量的定义 2.4.3 变量的初始化 2.4.4 变量的声明 2.4.5 变量的使用 2.5 读取键盘的输入 2.6

Java语言核心技术之基本程序设计

Java语言核心技术之基本程序设计 Java语言核心技术 2.1 标识符和关键字 标识符是在程序开发时给包,类.接口.注解,变量,方法命名的字符序列. 标识符在命名时需要遵守相关的命名规则和命名规范: 标识符的命名规则: 标识符不能是Java的关键字和保留字,关键字是Java中被赋予赋予特殊含义的单词,例如public static void等等.保留字是现有Java版本尚未使用,但是未来的Java版本可能会做关键字使用的单词,例如goto,const.但是可以包含关键字,保留字. 标识符可以由

Go语言核心技术(卷1):序-必读

Go语言核心技术系列文章都是我原创.或从国外的书籍和博客中收集优秀的文章进行翻译的,因此转载时请注明转载出处,不胜感激. 欢迎大家加入Go语言核心技术QQ群894864,里面热心大神很多哦!

C语言中将0到1000的浮点数用强制类型转换的方式生成一幅图像

搞过计算机图像的人都知道,图像中的每一个像素通常为一个整型数,它可以分成4个无符号的char类型,以表示其RGBA四个分量.一幅图像可以看做是一个二维整型数组.这里我会生成一个float数组,其数组大小为1000000,刚好1000*1000,数组内的浮点数的数值范围在0到1000.0之间,呈等差数组排列,相邻两数的差为0.001.然后将其每一个浮点数强制转化成一个整型数或三个unsigned char型,以决定像素的RGB三个通道分量,看看其生成的图像是什么样子. 前几天写了一篇文章是在C语言

读《java核心技术卷一》有感

过去一个多月了吧.才囫囵吞枣地把这书过了一遍.话说这书也够长的,一共706页.我从来不是个喜欢记录的人,一直以来看什么书都是看完了就扔一边去,可能有时候有那么一点想记录下来的冲动,但算算时间太紧,很多也是有始无终,毕竟在之前研究研究程序也只是自己的一个爱好而已,但没有想到签了一个程序员的工作.唉,这老天也太捉弄人了吧,让一个学电气工程(强电方向)学生毕业之后去写代码,而且是与硬件完全无关的代码.真是白念几年大学了.行了,就行发这么多牢骚吧. <java核心技术>有两个卷,我只看了卷一,从我的感

C语言中整数上溢,浮点数上溢,浮点数下溢问题

1.整数上溢的问题:在我的系统中int最大值为2147483647 ,如果出现整数上溢,运行结果为: 如果是无符号整数,出现整数上溢,运行结果为: 2.如果是浮点型,例如系统的最大值是3.4e38 ,如果*100,就会发生浮点数的上溢.本机运行结果为: 如果出现下溢,本机运行结果为: 出现NaN