Dart double学习

1,213 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

double

本篇笔记主要是学习dart中的double类型提供的相关方法。

相关常量

double类中,首先定义了一些常量,分别如下:

  //nan不为任何事物,包括nan,也即nan == nan 始终为false
  static const double nan = 0.0 / 0.0;
  //无穷大
  static const double infinity = 1.0 / 0.0;
  //负无穷大
  static const double negativeInfinity = -infinity;
  //double所能表示的最小数
  static const double minPositive = 5e-324;
  //double所能表示的最大数
  static const double maxFinite = 1.7976931348623157e+308;

对常量的一些操作:

  //nan始终不等于nan
  var a = double.nan;
  var b = a;
  print("${a == b}"); // false  
  
  //当对double所能表示的最小数再做减法的时候,比如double.minPositive - other,则最后输出的结果为`0 - other`,也就是`-other`
  print("最小数:${double.minPositive - 1}");//输出-1.0
  
  //当对double所能表示的最大数再加上一个数other时,最终输出的结果仍然为double.maxFinite
  print("最大数:${double.maxFinite + 100}");//输出仍然为double.maxFinite

基本运算

double类型支持+,-,*,/,~/,%这些基本操作,~/操作返回的是int类型,其余的操作返回的都是double类型。但是需要注意的是,在使用过程中发现,double类型容易丢失精度(这个问题在其他语言如java中同样存在),如下面的对1.2 / 0.2的计算:

1.2 / 0.2 = 5.999999999999999

所以在使用double类型的时候,需要注意精度的问题。

另外需要注意的是,~/会对除法操作之后取整,这里的取整是直接取整数部分,舍弃掉小数部分,如下:

1.2 ~/ 0.2 = 5

remainder(num other)取余

这个方法和%操作符的运算是一样的,同样需要注意丢失精度的问题:

1.2.remainder(0.2) = 0.1999999999999999

在这里需要注意取余的操作,其实取余的操作只是针对整数来做的,但是在dart中对double类型也进行了扩展,使其可以支持取余的操作,但是由于精度的问题,最后的计算结果可能会不准确,比如a除以b商q余r,那么计算结果仍然满足0 <= r < |b|,同时反向计算仍然会满足q * b + r = a这样的计算结果。需要注意的是这里的商q其实是a ~/ b的结果。

abs() 返回当前数的绝对值

void testAbs(double d) {
  var result = d.abs();
  print("${d}的绝对值:$result");
}

计算结果:

0.299的绝对值:0.299
-0.33的绝对值:0.33

sign

这是一个get函数,返回当前数据的符号,如果值小于0,则返回-1.0,如果值大于0,则返回+1.0,如果值是-0.0,0.0NaN,则返回本身。

void testSign(double d) {
  var result = d.sign;
  print("${d}.sign = ${result}\n");
}

运行结果如下:

-100.9.sign = -1.0

1.23.sign = 1.0

-0.0.sign = -0.0

0.0.sign = 0.0

NaN.sign = NaN

Infinity.sign = 1.0

-Infinity.sign = -1.0

5e-324.sign = 1.0

1.7976931348623157e+308.sign = 1.0

这里需要注意的是-0.0 == 0.0true

int round()

这个函数返回最接近当前数据的整数,也就是对当前数据进行四舍五入,需要注意的是NaN或者infinity不可以执行此方法,会抛出异常: Unsupported operation: Infinity or NaN toInt

void testRound(double d) {
  var result = d.round();
  print("$d.round = $result \n");
}

输出结果如下:

3.4.round = 3 

3.5.round = 4 

-3.4.round = -3 

-3.5.round = -4 

3.49.round = 3 

这里需要注意的是,负数对于四舍五入和正数的结果是相反的,我们可以将这个方法想象成一个坐标轴,然后在这个坐标轴上标出这个点,然后查看离这个点最近的那个整数。

int floor()

返回不大于当前数的最大整数,同样不支持infinityNaN调用此方法:

void testFloor(double d) {
  var result = d.floor();
  print("$d.floor == $result \n");
}

执行结果如下:

3.4.floor = 3 

3.5.floor = 3 

-3.4.floor = -4 

-3.5.floor = -4 

3.49.floor == 3

可以看到,这个方法相当于在坐标轴上始终从当前数的位置开始向左查找,直到找到第一个整数。

int ceil()

返回不小于当前数的最小整数,和floor()方法正好相反,同样不支持infinityNaN

void testCeil(double d) {
  var result = d.ceil();
  print("$d.ceil = $result \n");
}

执行结果如下:

3.4.ceil = 4 

3.5.ceil = 4 

-3.4.ceil = -3 

-3.5.ceil = -3 

3.49.ceil = 4 

可以看到,这个方法相当于从坐标轴当前数据的位置向右开始查找,直到找到第一个整数,也就是我们学过的进一法

int truncate()

这个方法会直接舍弃小数位,获取整数位,同样不支持infinityNaN

void testTruncate(double d) {
  var result = d.truncate();
  print("$d.truncate = $result \n");
}

执行结果如下:

3.4.truncate = 3 

3.5.truncate = 3 

-3.4.truncate = -3 

-3.5.truncate = -3 

3.49.truncate = 3 

0.001.truncate = 0 

double roundToDouble()

返回最靠近当前数的那个整数的double类型,在这种情况下,认为-0.0小于0.0,所以对于-0.5 < d < 0区间内的数使用这个函数将会返回-0.0

void testRoundToDouble(double d) {
  var result = d.roundToDouble();
  print("$d.roundToDouble = $result");
}

执行结果如下:

3.4.roundToDouble = 3.0
3.5.roundToDouble = 4.0
-3.4.roundToDouble = -3.0
-3.5.roundToDouble = -4.0
0.4.roundToDouble = 0.0
-0.4.roundToDouble = -0.0
-0.5.roundToDouble = -1.0
-0.4.roundToDouble() == 0.4.roundToDouble() is true

从上面的执行结果可以看出,对于-0.00.0本质上是相等的,只是在做运算的时候暂时认为-0.0小于0.0

使用这个方法对double.nandouble.infinity没有限制,这两个数执行这个方法的结果仍然是他本身。

double floorToDouble()

返回不大于当前数的最大整数,在这里也认为-0.0小于0.0。如果当前数已经是一个整数的double类型,则返回这个数本身,这也是为什么会有-0.0出现的原因。

0.0.floorToDouble = 0.0 

-0.0.floorToDouble = -0.0 

3.4.floorToDouble = 3.0 

3.5.floorToDouble = 3.0 

-3.4.floorToDouble = -4.0 

-3.5.floorToDouble = -4.0 

3.49.floorToDouble = 3.0 

1.0.floorToDouble = 1.0 

Infinity.floorToDouble = Infinity 

NaN.floorToDouble = NaN 

这个方法也不会受到double.infinitydouble.nan的影响。

double ceilToDouble()

返回比当前数大的那个最小的整数的double类型,也就是在坐标轴上从当前这个数的位置开始向右查找,找到的第一个整数的double类型。这个方法仍然认为-0.0小于0.0,对于-1.0 < d < 0.0区间内的数执行这个方法则会返回-0.0

void testCeilToDouble(double d) {
  var result = d.ceilToDouble();
  print("$d.ceilToDouble = $result \n");
}

执行结果如下:

3.4.ceilToDouble = 4.0 

3.5.ceilToDouble = 4.0 

-3.4.ceilToDouble = -3.0 

-3.5.ceilToDouble = -3.0 

-0.0.ceilToDouble = -0.0 

-0.2.ceilToDouble = -0.0 

0.0.ceilToDouble = 0.0 

Infinity.ceilToDouble = Infinity 

NaN.ceilToDouble = NaN 

可以看到,如果这个数已经是一个int类型数据的double类型,则执行ceilToDouble()方法返回当前数,double.infinitydouble.nan执行此方法也是返回这个数本身。

double truncateToDouble()

这个方法返回当前数据直接去除小数部分后整数部分的double类型。同样认为-0.0小于0.0,当一个数处于-1 < d < 0之间时,调用此方法返回-0.0,当一个数处于0 < d < 1时,调用此方法返回0.0

void testTruncateToDouble(double d) {
  var result = d.truncateToDouble();
  print("$d.truncateToDouble = $result \n");
}

执行结果如下:

3.4.truncateToDouble = 3.0 

3.5.truncateToDouble = 3.0 

-3.4.truncateToDouble = -3.0 

-3.5.truncateToDouble = -3.0 

-0.5.truncateToDouble = -0.0 

0.5.truncateToDouble = 0.0 

1.0.truncateToDouble = 1.0 

Infinity.truncateToDouble = Infinity 

NaN.truncateToDouble = NaN 

可以看到,这个方法也不受double.infinitydouble.nan的影响,会返回它们本身

toString()

返回当前数据的字符串形式,如果是非数字则返回NaN,对于正无穷大和负无穷大则分别返回infinity-infinity。当一个数据调用此方法获得一个字符串的时候,可以通过再调用double.parse()方法获得此数字。

3.4.toString = 3.4 

-3.4.toString = -3.4 

NaN.toString = NaN 

Infinity.toString = Infinity 

-Infinity.toString = -Infinity 

static double parse(String source,[double onError(String source)?])

这个方法用于将一个字符串转换成一个double类型的数据,输入参数source不能为空,当输入的参数不能转换为一个double类型的数据的时候,则会调用onError回调,不设置此参数则抛出FormatException异常。

void testParseDouble(String source) {
  var result = double.parse(source, (input) {
    print("$source无法转换为一个double类型的数字");
    return double.nan;
  });

  print("($source) 转换为double后的数据: $result \n");
}

最后执行的结果如下:

(3.4) 转换为double后的数据: 3.4 

(+3.4) 转换为double后的数据: 3.4 

(-0.0) 转换为double后的数据: -0.0 

(+0.0) 转换为double后的数据: 0.0 

(-.8) 转换为double后的数据: -0.8 

(.00) 转换为double后的数据: 0.0 

(        9.9) 转换为double后的数据: 9.9 

(1.2e3) 转换为double后的数据: 1200.0 

(3e-3) 转换为double后的数据: 0.003 

nan无法转换为一个double类型的数字
(nan) 转换为double后的数据: NaN 

(Infinity) 转换为double后的数据: Infinity 

(-Infinity) 转换为double后的数据: -Infinity 

(-NaN) 转换为double后的数据: NaN 

hello world无法转换为一个double类型的数字
(hello world) 转换为double后的数据: NaN 

可以看到,只要传入的参数基本符合double类型的数据就可以转换为正确的double数据,不是正确的double类型的数据则会调用onError回调进行处理。

需要注意的是,这个方法已经被标记为过期了,后面可能会被删除,需要用tryParse(String source)来代替这个方法。

static double? tryParse(String source)

这个方法尝试将一个字符串转换为double类型的数据,如果输入的参数不能转换为一个double类型的数据,则会返回null(果然还是向着kotlin靠拢了)。输入的参数不能为空。

(3.4).tryParse = 3.4 

(+3.4).tryParse = 3.4 

(-3.4).tryParse = -3.4 

(+0.0).tryParse = 0.0 

(-0.0).tryParse = -0.0 

(  .09).tryParse = 0.09 

(1..).tryParse = null 

(3e-3).tryParse = 0.003 

(        0.009).tryParse = 0.009 

(NaN).tryParse = NaN 

(-NaN).tryParse = NaN 

(-Infinity).tryParse = -Infinity 

(hello world).tryParse = null 

如果输入的参数为null,则会抛出NoSuchMethodError: The getter 'length' was called on null.异常。