好的,所以我们都知道浮点数问题,比如:
0.23 - 1 + 1 = 0.22999999999999998
由于在javascript中,与其他语言不同,所有的数字实际上都是浮点,并且没有int/decimal,所以有各种各样的库和解决方法,如BigDecimal来处理这个问题。这一点最好在这里讨论。
我正在创建一个支持浮点数字的"数字微调器"控件,很明显,我不希望用户单击"向上"然后单击"向下",得到一个与他开始时不同的数字,所以我尝试编写一个变通方法-某种"addPrecise"方法。
我的想法如下:
- 取我要加的两个数字,计算出它们的"精度"——小数点后有多少位数
- 将这两个数字作为浮点数相加
- 将
toFixed()
应用于两个结果的最大精度
例如:
float #1: 1.23
float #2: -1
添加它们通常会得到0.22999999999999998
,但如果我取最大小数位数,即#1的2位数字,并应用toFixed(2)
,我会得到我想要的0.23
。
我已经用下面的一段代码做到了这一点,但我对此不满意。
function addPrecise(f1, f2){
var floatRegex = /[^d-]/;
var f1p = floatRegex.exec(f1.toString()) ? f1.toString().split(floatRegex.exec(f1.toString()))[1].length : 0;
var f2p = floatRegex.exec(f2.toString()) ? f2.toString().split(floatRegex.exec(f2.toString()))[1].length : 0;
var precision = Math.max(f1p,f2p);
return parseFloat((parseFloat(f1) + parseFloat(f2)).toFixed(precision));
}
(值得注意的是,我使用正则表达式来查找"浮点",因为在其他地区,它可能是逗号而不是句点。我还考虑了我得到Int(无点)的可能性,在这种情况下,它的精度为0。)
有没有更干净/更简单/更好的方法来做这件事?
编辑:我还想指出,找到一种可靠的方法来提取小数位数也是问题的一部分。我不确定我在那里的方法。
这个问题的合适解决方案是:
- 确定小数点后需要多少位数字,例如d
- 读取用户的输入,并将其转换为按10d缩放的整数
- 用整数做所有算术运算
- 为用户显示值时,将其除以10d以进行显示
更多详细信息:
- 如果所有工作都以用户输入的数据为单位,则小数点后所需的位数d与用户接受的位数相同。(例如,如果要用步长的分数进行一些算术运算,那么可能需要更多的数字。)
- 当用户输入输入时,应该将其转换为缩放的整数。请注意,用户输入一个字符串(不是浮点数;当用户键入它时,它只是一个字符串),并且该字符串应该是一个数字(表示数字的字符)。处理此输入的一种方法是调用库例程将此数字转换为浮点数,然后乘以10d,然后将浮点结果四舍五入到最接近的整数(以补偿浮点舍入误差)。对于大小合理的数字,这将始终产生所需的结果;浮点舍入错误不会成为问题。(应该拒绝过大的用户输入。)处理此输入的另一种方法是编写自己的代码,将数字直接转换为缩放整数,完全避免浮点运算
- 只要只使用加法、乘法和减法(例如,将步长乘以多个步长并与先前值相加),并且不超过整数范围,则所有算术都是精确的;不存在舍入误差。如果使用分裂,就必须重新考虑这种做法
- 与读取输入一样,显示输出可以在浮点和库例程的帮助下执行,也可以编写自己的代码直接转换。要使用浮点,请将整数转换为浮点,除以10d,然后使用库例程将其格式化为小数点后不超过d位数的。(在库例程中使用默认格式转换可能会导致显示超过d的数字。如果只显示d数字,并且d-是一个相当小的数字,则格式化过程中发生的浮点舍入错误将不可见。)
只要注意确保算术在合理的范围内完全使用整数(或浮点格式中可以精确表示的其他值),就可以仅使用浮点来实现上述所有操作。