四舍五入到重要数字 - 缺少零

时间:2014-06-06 15:16:03

标签: javascript math

我目前正在制作一个JavaScript驱动的数学软件包,专注于舍入到各种重要数字(SF),但我遇到了一个问题,我'我正在努力解决。

稍后会详细介绍此问题,但首先要为您提供一些背景信息。

该程序旨在选择给定范围内的完全随机数,然后自动计算该数字的相关有效数字;例如:

随机数: 0.097027 S.Fs: 9, 7, 0, 2, 7


以下是我为您提供视觉表示的截图:

enter image description here

正如您所看到的,一旦用户选择了他们的号码,他们就有机会点击四个单独的'SF'按钮来查看他们提供给1,2,3的随机数和4个S.Fs分别。

对于每个SF(1-4),随机数向下舍入向上舍入并且四舍五入 X SF 以及下面的比例为用户提供了更直观的演示,以显示程序选择SF值的原因。

我已经为此编写了绝大多数代码并对其进行了测试,到目前为止,数字正在出现,我期待它们如何。 差不多......

在我给出(0.097027)的例子中;正如您在我所包含的图像中看到的那样,4 S.F的数据绝对正确并准确输出。

当我点击 3 SF 按钮时,我希望看到以下内容:

随机数: 0.097027 3 S.F圆形上/下/下: 0.0970

然而,我实际得到的是:

随机数: 0.097027 3 S.F圆形上/下/下: 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);
};

//---------------------------------------------------------------------------------

更新

我仍在继续尝试找到解决此问题的方法,我最近采用的方法是将我随机生成的数字转换为可搜索的字符串变量,然后使用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'

然后我可以进一步定位我的搜索,以确定我的第一个有效数字是否在其后面的数字中有任何“有问题”的零。

如果有,那么我将一个布尔变量设置为'true',然后在一个单独的函数中创建我的舍入/关闭/向上舍入数字的更多文本字符串,这样我就可以实际选择添加一个'0'到现有数字字符的末尾。

这种方法对我来说在孤立的情况下很有用,但随着我的随机数长度从5到12位不等,它仍然没有处理所有场景。

也许我需要创建一个动态的toFixed(i)函数?任何想法都会受到欢迎。

2 个答案:

答案 0 :(得分:4)

您可以直接管理字符串,而不是使用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);
    }
}

答案 1 :(得分:0)

我已经离开了,我想我现在终于解决了我最初的问题。

我在使用toFixedtoPrecision时存在一定程度的混淆。我以前曾尝试将向上舍入,向下和向下数字转换为字符串,然后搜索其中的每一个以查找小数点("."),然后计算出尾随数字的数量,然后生成正确的toFixed点。

然而,考虑到我的随机数最多可达12位数,这非常受欢迎,因此我现在所做的就是正确使用toPrecision。对于每个SF按钮&#39; (1-4)我使用了相应的toPrecision点,例如SF1:

    sfRoundedTextBlock.innerHTML = sfRoundedTextBlock.innerHTML + '<p><strong>Number: </strong></br>' + String(genNumber) + 
    '</br>' + '<strong>Rounded down to ' + i + ' SF:</br></strong>' + downArray[i].toPrecision(1) + '</br>' + 
    '<strong>Rounded up to ' + i + ' SF:</br></strong>' + upArray[i].toPrecision(1) + '</br><strong>Rounded off to ' + i + ' SF:</br></strong>' 
    + roundedArray[i].toPrecision(1) + '</br>' + '(See the scale below for why we choose <strong>' + roundedArray[i].toPrecision(1) + '</strong> as the rounded off value.)</p>';

    //Add The new scale data (Rounded Down)
    downTextBlock = document.getElementById('down');
    document.getElementById("down").innerHTML = String(downArray[i].toPrecision(1));

    //Add The new scale data (Rounded Up)
    upTextBlock = document.getElementById('up');
    document.getElementById("up").innerHTML = String(upArray[i].toPrecision(1));

现在每次都给我准确的结果,但仍然有一个障碍可以跳。偶尔我会得到一个随机的场景,其中科学记数法必须包含在我输出的答案中,例如21819向下舍入为1 SF,将在2e+4而不是20000读取。

为了解决这个问题,我将我的向上和向下的数字设置为可搜索的字符串,然后查看这些字符以查找任何非法/科学字符[a-z]。如果我找到了,我执行了一个稍微不同的输出版本,它使用了parseFloat,它删除了科学记数法并显示了正确的数字:

//Convert Up, Down and Rounded into Strings based on their precision
        var upString = String(upArray[i].toPrecision(1));
        var downString = String(downArray[i].toPrecision(1));
        var roundedString = String(roundedArray[i].toPrecision(1));
        //Set up a regexp to search for characters [a-z], i.e. non-numeric
        var regexp = /[a-z]/g; 
        //Search the up, down and rounded strings for non-numeric characters
        var upResult = upString.match(regexp);
        var downResult = downString.match(regexp);
        var roundedResult = roundedString.match(regexp);
        //If any of these strings contain a letter (non-numeric) we need to add in parseFloat to strip away the scientific notation included.
        var containsChar = false;

        if (upResult != null || downResult != null || roundedResult != null)

            {
                containsChar = true;
                //alert("There is SN included here");
            }

         //Add The new data
         sfRoundedTextBlock = document.getElementById('SFRounded');

        if (containsChar == true)

        {
            sfRoundedTextBlock.innerHTML = sfRoundedTextBlock.innerHTML + '<p><strong>Number: </strong></br>' + String(genNumber) + 
            '</br>' + '<strong>Rounded down to ' + i + ' SF:</br></strong>' + parseFloat(downArray[i].toPrecision(1)) + '</br>' + 
            '<strong>Rounded up to ' + i + ' SF:</br></strong>' + parseFloat(upArray[i].toPrecision(1)) + '</br><strong>Rounded off to ' + i + ' SF:</br></strong>' 
            + parseFloat(roundedArray[i].toPrecision(1)) + '</br>' + '(See the scale below for why we choose <strong>' + parseFloat(roundedArray[i].toPrecision(1)) + '</strong> as the rounded off value.)</p>';

            //Add The new scale data (Rounded Down)
            downTextBlock = document.getElementById('down');
            document.getElementById("down").innerHTML = String(parseFloat(downArray[i].toPrecision(1)));

            //Add The new scale data (Rounded Up)
            upTextBlock = document.getElementById('up');
            document.getElementById("up").innerHTML = String(parseFloat(upArray[i].toPrecision(1)));
        }  

对此进行了广泛的测试,它似乎正如希望的那样工作。