我目前正在制作一个 JavaScript 驱动的数学包,其重点是舍入到各种有效数字 (SF),但我遇到了一个正在努力解决的问题。
稍后会详细讨论这个问题,但首先为您提供一些背景知识。
该程序的目的是在给定范围内选择一个完全随机的数字,然后自动计算出该数字的相关有效数字;例如:
随机数: 0.097027 S.Fs: 9, 7, 0, 2, 7
这是我制作的屏幕截图,为您提供直观的表示:
如您所见,一旦用户选择了号码,他们就有机会单击四个单独的“SF”按钮来查看分别呈现给 1、2、3 和 4 个 S.F 的随机号码。
对于每个 SF (1-4),随机数向下舍入、向上舍入并四舍五入到X SF,下面的比例为用户提供了更直观的呈现,以显示程序选择 SF 值的原因。
我已经为此编写了绝大多数代码并对其进行了测试,到目前为止,数字正如我所期望的那样。嗯,差不多...
在我给出的例子中(0.097027);正如您在我包含的图像中看到的,4 SF 的数据绝对正确并且输出准确。
当我单击3 SF按钮时,我希望看到以下内容:
随机数: 0.097027 3 SF 向上/向下/关闭: 0.0970
然而,我实际得到的是:
随机数: 0.097027 3 SF 向上/向下/关闭: 0.097
该程序尚未显示附加零。这是我的程序中以零结尾的数字的完美示例,在这种情况下,零非常重要并且必须显示。
数据通常是正确的,但在正确的时间输出有效的零似乎存在问题。我研究了该toFixed(x)方法,如果我分配,toFixed(4)我会得到正确的所需输出,但因为我的数字每次都是随机生成的,所以它们的长度范围可以是 5 位数字,例如89.404最多 > 10,例如 `0.000020615。
所以看起来该 toFixed 方法需要灵活/动态,例如 toFixed(n) 预先运行一个函数来确定到底需要多少个尾随零?
以下是我当前解决方案的一些关键摘录,供您考虑:
function generateNum() {
do {
genNumber = Math.random() * Math.pow (10, randomRange(-5, 5));
//Round
genNumber = roundToNSF(genNumber, 5, 0);
// This number must contain >1 digit which is 1 to 9 inclusive otherwise we may have e.g. 100. Rounding 100
}
while (!countNonZero(genNumber) || genNumber < 1E-05 || genNumber == 0);
//Round
genNumber = roundToNSF(genNumber, 5, 0);
genNumber = String(genNumber);
genNumber = Number(genNumber);
}
//----------------------------------------------------------------------------
function randomRange(min, max) {
/**
* Returns a random integer between min (inclusive) and max (inclusive)
* Using Math.round() will give you a non-uniform distribution!
*/
return Math.floor(Math.random() * (max - min + 1)) + min;
}
//---------------------------------------------------------------------------
//Click SF3 Button to reveal the data
function showSF3() {
//Remove any CSS properties on the buttons from previous use
removeButtonCSS();
document.getElementById('SFRounded').style.display = "block";
document.getElementById('scale').style.display = "block";
document.getElementById("SF3").className = document.getElementById("SF3").className + "buttonClick"; // this removes the blue border class
//Clear text
deleteRounded();
deleteScale();
//Run calculation
calculateAnswer();
//alert(genNumber.toFixed(4));
for (i = 3; i < 4; i++)
{
//Add The new data
sfRoundedTextBlock = document.getElementById('SFRounded');
//alert(downArray[i].toFixed(4));
//Data output to HTML.
sfRoundedTextBlock.innerHTML = sfRoundedTextBlock.innerHTML + '<p><strong>Number: </strong></br>' + String(genNumber) +
'</br>' + '<strong>Rounded down to ' + i + ' SF:</br></strong>' + downArray[i] + '</br>' +
'<strong>Rounded up to ' + i + ' SF:</br></strong>' + upArray[i] + '</br><strong>Rounded off to ' + i + ' SF:</br></strong>'
+ roundedArray[i] + '</br>' + '(See the scale below for why we choose <strong>' + roundedArray[i] + '</strong> as the rounded off value.)</p>';
}
}
//----------------------------------------------------------------------
var roundedArray = [];
var upArray = [];
var downArray = [];
var temp;
function calculateAnswer() {
//Clear Arrays
roundedArray = [];
upArray = [];
downArray = [];
// Work out the answer:
for (i = 0; i < 4; i++) {
var nSF = i + 1;
// Round OFF ...
temp = roundToNSF(genNumber, nSF, 0);
// We actually have to do this twice ...
roundedArray[nSF] = roundToNSF(temp, nSF, 0);
// Round UP ...
upArray[nSF] = roundToNSF(genNumber, nSF, 1);
// Round DOWN ...
downArray[nSF] = roundToNSF(genNumber, nSF, -1);
// e.g. x = 0.0098 rounded to 1SF is 0.010 initially (take the log of 0.0098 and try it!).
};
};
//-------------------------------------------------------------------------
//Globals
var aNumber;
var digits;
var way;
function roundToNSF(aNumber, digits, way){
// Round a number to n significant figures (can use roundToNDP provided we know how many decimal places):
if (way == undefined) { way = 0; }; // default is round off
if (aNumber !=0) {
if (aNumber > 0)
{
z = log10(aNumber);
}
else
{
z = log10(-aNumber);
};
z = Math.floor(z);
var nDP = digits - z - 1; // Rounding to nDP decimal places is equivalent to rounding to digits significant figures ...
var roundedNumber = roundToNDP(aNumber, nDP, way);
}
else {
roundedNumber = aNumber; // Number is zero ...
};
return Number(roundedNumber);
};
//---------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
更新:
我仍在继续尝试找到这个问题的解决方案,我最近采取的方法是将随机生成的数字转换为可搜索的字符串变量,然后使用命令indexOf(".")查找小数点(dp)的位置的位置。
然后我搜索了我的数字,从 dp 的位置开始,找到第一个重要的非零数字 [1-9]。
var genNumber = 0.097027;
var rString = String(genNumber);
var positionofDP = rString.indexOf(".");
var regexp = /[1-9]/;
var positionofNonZero = Number(rString.search(regexp, positionofDP)); // Output would be '5'
Run Code Online (Sandbox Code Playgroud)
然后,我能够进一步确定我的搜索目标,以确定我的第一个有效数字后面的直接数字中是否有任何“有问题”的零。
如果有的话,我将一个布尔变量设置为“true”,然后在一个单独的函数中创建进一步的四舍五入/向下/向上数字的文本字符串,这样我就可以物理上选择在现有数字字符的末尾。
这种方法在个别情况下确实对我有用,但由于我的随机数长度范围为 5-12 位数字,它仍然无法处理所有情况。
也许我需要创建一个动态toFixed(i)函数?任何想法都会受到极大的欢迎。
您可以直接管理字符串,而不是使用 Int 上的固定点。
这是一个小小提琴的链接:http://jsfiddle.net/5rw5G/4/
这并不是为了完全/准确地解决您的问题,但可能会帮助您找到另一种解决方案。
function getRoundedSFs(num, SFCount) {
// Match every "leading zeros" before and after the .
var matches = num.toString().match(/^-?(0+)\.(0*)/);
// starting with "0."
if (matches) {
var firstIndex = matches[0].length;
var prefix = matches[0];
sf = Number(num.toString().substring(firstIndex, firstIndex + SFCount + 1));
sf = Math.round(sf / 10);
sf = prefix + sf.toString();
return Number(sf).toFixed(matches[2].length+SFCount);
}
// starting with something else like -5.574487436097115
else {
matches = num.toString().match(/^(-?(\d+))\.(\d+)/);
var decimalShift = SFCount - matches[2].length;
var rounded = Math.round(num * Math.pow(10, decimalShift));
rounded /= Math.pow(10, decimalShift);
return rounded.toFixed(decimalShift);
}
}
Run Code Online (Sandbox Code Playgroud)