本文主要关注NP.times(0.362, 100);
这个函数的实现原理:
number_precision的功能列举如下:
import 'package:number_precision/number_precision.dart';
NP.strip(0.09999999999999998); // = 0.1
NP.plus(0.1, 0.2); // = 0.3, not 0.30000000000000004
NP.plus(2.3, 2.4); // = 4.7, not 4.699999999999999
NP.minus(1.0, 0.9); // = 0.1, not 0.09999999999999998
NP.times(3, 0.3); // = 0.9, not 0.8999999999999999
NP.times(0.362, 100); // = 36.2, not 36.199999999999996
NP.divide(1.21, 1.1); // = 1.1, not 1.0999999999999999
NP.round(0.105, 2); // = 0.11, not 0.1
下面是整个源码实现:
import 'dart:math';
/// The smallest possible value of an int within 64 bits.
const int intMinValue = -9007199254740991;
/// The biggest possible value of an int within 64 bits.
const int inMaxValue = 9007199254740991;
class NP {
static bool _boundaryCheckingState = true;
/// 字符串转为[num]类型
/// [number] 数据
static num parseNum(dynamic number) {
if (number is num) {
return number;
} else if (number is String) {
return num.parse(number);
} else {
throw FormatException('$number is not of type num and String');
}
}
/// 把错误的数据转正
/// strip(0.09999999999999998)=0.1
/// [number] 数据 [precision] 截取小数位
static num strip(dynamic number, {int precision = 14}) {
return num.parse(parseNum(number).toStringAsFixed(precision));
}
/// 返回小数的位数
/// [number] 数据
static num digitLength(dynamic number) {
final eSplit = parseNum(number).toString().toLowerCase().split('e');
final digit = eSplit[0].split('.');
final len = (digit.length == 2 ? digit[1].length : 0) -
(eSplit.length == 2 ? int.parse(eSplit[1]) : 0);
return len > 0 ? len : 0;
}
/// 把小数转成整数,支持科学计数法。如果是小数则放大成整数
/// [number] 数据
static num float2Fixed(dynamic number) {
final dLen = digitLength(number);
if (dLen <= 20) {
if (number is String) {
if (number.toLowerCase().indexOf('e') == -1) {
return num.parse(number.replaceAll('.', ''));
}
return num.parse(num.parse(number)
.toStringAsFixed(dLen as int)
.replaceAll(dLen == 0 ? '' : '.', ''));
} else if (number is num) {
return num.parse(number
.toStringAsFixed(dLen as int)
.replaceAll(dLen == 0 ? '' : '.', ''));
}
throw FormatException('$number is not of type num and String');
}
throw Exception(
'$number is beyond boundary when transfer to integer, the results may not be accurate');
}
/// 检测数字是否越界,如果越界给出提示
/// [number] 数据
static void checkBoundary(dynamic number) {
if (_boundaryCheckingState) {
if (number > inMaxValue || number < intMinValue) {
throw Exception(
'$number is beyond boundary when transfer to integer, the results may not be accurate');
}
}
}
/// 精确乘法
/// [num1] 左操作数 [num2]右操作数
/// [others] 更多操作数使用数组传递
///
/// 譬如 times(1, 2, [22,33])
static num times(dynamic num1, dynamic num2, [List<dynamic>? others]) {
if (others != null) {
return times(times(num1, num2), others[0],
others.length >= 2 ? others.sublist(1) : null);
}
num num1Changed = float2Fixed(num1);
num num2Changed = float2Fixed(num2);
num baseNum = digitLength(num1) + digitLength(num2);
dynamic leftValue = num1Changed * num2Changed;
checkBoundary(leftValue);
return NP.strip(leftValue / pow(10, baseNum));
}
/// 精确加法
static num plus(dynamic num1, dynamic num2, [List<dynamic>? others]) {
if (others != null) {
return plus(plus(num1, num2), others[0],
others.length >= 2 ? others.sublist(1) : null);
}
num baseNum = pow(10, max(digitLength(num1), digitLength(num2)));
return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
}
/// 精确减法
static num minus(dynamic num1, dynamic num2, [List<dynamic>? others]) {
if (others != null) {
return minus(minus(num1, num2), others[0],
others.length >= 2 ? others.sublist(1) : null);
}
num baseNum = pow(10, max(digitLength(num1), digitLength(num2)));
return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
}
/// 精确除法
static num divide(dynamic num1, dynamic num2, [List<dynamic>? others]) {
if (others != null) {
return divide(divide(num1, num2), others[0],
others.length >= 2 ? others.sublist(1) : null);
}
num num1Changed = float2Fixed(num1);
num num2Changed = float2Fixed(num2);
checkBoundary(num1Changed);
checkBoundary(num2Changed);
return times(num1Changed / num2Changed,
(pow(10, digitLength(num2) - digitLength(num1))));
}
/// 四舍五入
static num round(dynamic number, int ratio) {
num base = pow(10, ratio);
return divide((times(number, base).round()), base);
}
/// 是否进行边界检查,默认开启
/// [flag] 标记开关,true 为开启,false 为关闭,默认为 true
static void enableBoundaryChecking([flag = true]) {
_boundaryCheckingState = flag;
}
}
在Flutter中,pow函数是Dart数学库中的一个函数,用于计算一个数字的指数幂。具体而言,pow函数的定义如下:
double pow(num x, num exponent)
其中,x是底数,exponent是指数。这函数返回x的exponent次方的结果。
例如,如果你想计算2的3次方,可以这样使用:
import 'dart:math';
void main() {
double result = pow(2, 3);
print(result); // 输出 8.0
}
这段代码中,pow(2, 3)的结果是2的3次方,即8.0。需要注意的是,pow函数返回的结果是double类型。
回到:
static num times(dynamic num1, dynamic num2, [List<dynamic>? others]) {
if (others != null) {
return times(times(num1, num2), others[0],
others.length >= 2 ? others.sublist(1) : null);
}
num num1Changed = float2Fixed(num1); //小数转化为整数,比如1.234567890 则为1234567890
num num2Changed = float2Fixed(num2);//跟上相同
num baseNum = digitLength(num1) + digitLength(num2);//小数点后面的位数,1.234567890 位数为9
dynamic leftValue = num1Changed * num2Changed; //整数想乘
checkBoundary(leftValue);
return NP.strip(leftValue / pow(10, baseNum));//结果除以 10的baseNum次幂。即还原之前放大的倍数。然后转化为num.parse输出为num类型
}
分析逻辑步骤:
- 如果others不为null,递归调用times函数,将结果与others中的元素相乘,直到处理完所有元素。
- 将输入的num1和num2转换为整数形式,使用float2Fixed函数。
- 计算乘积的基数baseNum,即两个操作数小数位数之和,用于后续处理。
- 计算整数形式的乘积leftValue。
- 调用checkBoundary函数检查乘积是否越界。
- 将乘积还原成小数形式,使用NP.strip函数,并返回结果。
总体而言,times函数的主要逻辑是将两个数转换为整数形式,进行整数乘法,然后将结果还原成小数形式,同时处理了越界的情况。
好的,到此分析完毕。