1

Possible Duplicate:
Is JavaScript's Math broken?

The user enters values in the first two text boxes and, as they type, Javascript (sorry, no jQuery, I'm not up to it yet) is used to calculate the precise sum and the sum rounded to 2 digits.

Why am I getting rounding error and what can I do to correct it?

Many thanks.

Hmmm....ParseFloat? Wrong data type?

What I would like to see if the precise answer as if it were added on a calculator. Is there a parseDecimal or other data type that I can use?

![enter image description here][1]

    function SumValues() {
        //debugger;
        var txtSubsContrRbtAmt = document.getElementById("<%=txtSubsContrRbtAmt.ClientID%>");
        var txtDeMinAmt = document.getElementById("<%=txtDeMinAmt.ClientID%>");
        var txtTotRbtAmt = document.getElementById("<%=txtTotRbtAmt.ClientID%>");
        var txtRndRbtAmt = document.getElementById("<%=txtRndRbtAmt.ClientID%>");

        var total = Add(txtSubsContrRbtAmt.value, txtDeMinAmt.value);

        txtTotRbtAmt.value = total;
        txtRndRbtAmt.value = RoundToTwoDecimalPlaces(total);
    }

    function Add() {
        var sum = 0;
        for (var i = 0, j = arguments.length; i < j; i++) {
            var currentValue;
            if (isNumber(arguments[i])) {
                currentValue = parseFloat(arguments[i]);
            }
            else {
                currentValue = 0;
            }

            sum += currentValue;
        }
        return sum;
    }

    function RoundToTwoDecimalPlaces(input) {

        return Math.round(input * 100) / 100
    }

    function IsNumeric(input) {
        return (input - 0) == input && input.length > 0;
    }

    function isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

  [1]: https://i.stack.imgur.com/5Otrm.png

Update. I am evaluating something like this:

function AddWithPrecision(a, b, precision) {
        var x = Math.pow(10, precision || 2);
        return (Math.round(a * x) + Math.round(b * x)) / x;
    }
Community
  • 1
  • 1
Chad
  • 23,658
  • 51
  • 191
  • 321
  • What is your expecting result ? – The Alpha Aug 02 '12 at 03:43
  • 1
    I'd suggest passing the maximum number of decimal places entered to .toFixed() then trimming zeroes from the right side. – Kevin Stricker Aug 02 '12 at 03:46
  • 20.013. But if they entered more significant digits after the decimal, the calculated sum should include those, too. Use Windows Calculator to test. What it shows, is what I want. I'm stunned that this is not an easy thing. Javascript disappoints me at every turn. – Chad Aug 02 '12 at 03:47
  • I like your suggestion. I was thinking along those terms, too. Can you or someone help with the logic? This is pushing my capabilities... – Chad Aug 02 '12 at 03:49
  • It's not javascript's problem. It's a problem with your misunderstanding of how floating point numbers work. You'll have the same result in any other programming language: C, Perl, Ruby etc. The programmers who wrote Windows Calculator had to do the calculation without the help of the programming language. That is, the numbers are stored as a data structure rather than a floating point number. – slebetman Aug 02 '12 at 03:50
  • @Chad: See the link in mootinator's comment. – slebetman Aug 02 '12 at 03:53
  • I understand that that this issue comes with teh float data type. The reason I am disappointed is that I don't see a decimal data type (parseDecimal) alternative. – Chad Aug 02 '12 at 03:54
  • @Chad: The CPU does not have a decimal data type. Float is what engineers, mathematicians and physicists agreed upon back in the 80s for how to implement decimals in computers (earlier computers like IBM s360 did implement fixed point). Therefore very few programming languages (almost none of the modern ones) will do what you want. I guess you shouldn't really be disappointed with javascript but rather with computers in general. – slebetman Aug 02 '12 at 04:00
  • @slebetman-It's been a while since I studied the internal representation of floats in school, granted, but I think you might be carrying the point a little too far. I'm just looking for the language to handle the details for me the way C#'s decimal data type does. If I am happy with the simplicity of the code to accomplish this on the server side using C# but not yet happy with the way Javascript handles it, it must not be that I am disappointed with computers in general, but instead have a beef with Javascript. A beef that doesn't start here. – Chad Aug 02 '12 at 04:23

2 Answers2

3

There is a golden rule for anyone writing software in the financial sector (or any software dealing with money): never use floats. Therefore most software dealing with money use only integers and represent decimal numbers as a data structure.

Here's one way of doing it:

(Note: this function adds two strings that looks like numbers)

(Additional note: No error checking is done to aid clarity. Also does not handle negative numbers)

function addNumberStrings (a,b) {
    a = a.split('.');
    b = b.split('.');
    var a_decimal = a[1] || '0';
    var b_decimal = b[1] || '0';
    diff = a_decimal.length - b_decimal.length;
    while (diff > 0) {
        b_decimal += '0';
        diff --;
    }
    while (diff < 0) {
        a_decimal += '0';
        diff ++;
    }
    var decimal_position = a_decimal.length;

    a = a[0] + a_decimal;
    b = b[0] + b_decimal;

    var result = (parseInt(a,10)+parseInt(b,10)) + '';

    if (result.length < decimal_position) {
        for (var x=result.length;x<decimal_position;x++) {
            result = '0'+result;
        }
        result = '0.'+result
    }
    else {
        p = result.length-decimal_position;
        result = result.substring(0,p)+'.'+result.substring(p);
    }
    return result;
}

*note: code is simplified, additional features left out as homework.

slebetman
  • 109,858
  • 19
  • 140
  • 171
0

To fix your addition the way you want, I'd suggest counting the decimal places in each number somehow This method, for instance Then passing the max value to toFixed, and trimming any leftover zeroes.

function AddTwo(n1, n2) {
    var n3 = parseFloat(n1) + parseFloat(n2);
    var count1 = Decimals(n1, '.');
    var count2 = Decimals(n2, '.');
    var decimals = Math.max(count1, count2);
    var result = n3.toFixed(decimals)
    var resultDecimals = Decimals(result, '.');
    if (resultDecimals > 0) {
        return result.replace(/\.?0*$/,'');
    }
    else {
        return result;
    }
}

// Included for reference - I didn't write this
function Decimals(x, dec_sep)
{
    var tmp=new String();
    tmp=x;
    if (tmp.indexOf(dec_sep)>-1)
        return tmp.length-tmp.indexOf(dec_sep)-1;
    else
        return 0;
} 

Here's a JSFiddle of that

Kevin Stricker
  • 17,178
  • 5
  • 45
  • 71