16

I'm very new to html, javascript, and css so please forgive if my question sounds idiotic to you. My question is how can I prevent the function toFixed() from rounding of the decimal number.

Here's my link: http://jsfiddle.net/RWBaA/4/

What I'm trying to do is I'm checking the input if its a valid decimal number whenever the user types in the textbox. At the same time I also want to check if the input is a valid currency which means it can only add two more numbers at the right of the decimal point. The problem is when the user enters the 3rd number after the decimal point the 2nd number after the decimal point is rounded off to the nearest hundredths if the the 3rd number is >= 5.

Test Input :

  Input         Output  
123456.781 -> 123456.78

123456.786 -> 123456.79

Why my code does not allow arrow keys in chrome?

Please help. If you have a better solution you are free to suggest. Thanks in advance.

George Kagan
  • 5,913
  • 8
  • 46
  • 50
TheOnlyIdiot
  • 1,182
  • 7
  • 17
  • 37
  • [Very related](https://stackoverflow.com/q/4187146/5267751). In this particular case because the input is already a string it doesn't need to be converted to a float, but there are some duplicate answers. – user202729 Jun 25 '18 at 14:43

7 Answers7

20

That's even simpler:

function truncateToDecimals(num, dec = 2) {
  const calcDec = Math.pow(10, dec);
  return Math.trunc(num * calcDec) / calcDec;
}

So:

truncateToDecimals(123456.786) -> 123456.78
David
  • 623
  • 7
  • 16
  • does not always work. 4492903605.9 produces 4492903605.89 – elmango Aug 22 '18 at 21:21
  • That's because you are adding a new decimal, since the default value for dec parameter is 2. You can whether pass dec=1 or fix the function to take the default decimals length from the passed number. – David Aug 23 '18 at 14:56
  • A possible caveat here, the function will less decimals in case the are zeros (i.n.: 2.20 will be 2.2). But in my opinion that's ok, since is the way javascript works, right? respect. I mean, you can return toFixed() instead, but in that case the returned type will be a string which I don't see fair here. In case you want to do that, do it outside the function, let say, in the UI side, and keep to numeric logic clean. That way you'll prevent bugs and ugly artifacts in the future. – David Jul 14 '19 at 12:21
  • `truncateToDecimals(99999999999999.9999,2)` returns `100000000000000` – FaizanHussainRabbani Aug 30 '21 at 19:08
11

Round the number (down) to the nearest cent first:

val = Math.floor(100 * val) / 100;

EDIT It's been pointed out that this fails for e.g. 1.13. I should have known better myself!

This fails because the internal floating point representation of 1.13 is very slightly less than 1.13 - multiplying that by 100 doesn't produce 113 but 112.99999999999998578915 and then rounding that down takes it to 1.12

Having re-read the question, it seems that you're really only trying to perform input validation (see below), in which case you should use normal form validation techniques and you shouldn't use .toFixed() at all. That function is for presenting numbers, not calculating with them.

$('#txtAmount').on('keypress', function (e) {
    var k = String.fromCharCode(e.charCode);
    var v = this.value;
    var dp = v.indexOf('.');

    // reject illegal chars
    if ((k < '0' || k > '9') && k !== '.') return false;

    // reject any input that takes the length
    // two or more beyond the decimal point
    if (dp >= 0 && v.length > dp + 2) {
        return false;
    }

    // don't accept >1 decimal point, or as first char
    if (k === '.' && (dp >= 0 || v.length === 0)) {
        return false;
    }
});
Alnitak
  • 334,560
  • 70
  • 407
  • 495
7

you can give this a try, it won't round your decimals

/**
 * @param {any} input 
 * @param {number} decimals 
 */
var toFixed = function(input, decimals) {
  var arr = ("" + input).split(".");
  if (arr.length === 1) return input;
  var int = arr[0],
      max = arr[1].length,
      dec = arr[1].substr(0, decimals > max ? max : decimals);
  return decimals === 0 ? int : [int, "." , dec].join("");
}
Santiago Hernández
  • 5,438
  • 2
  • 26
  • 34
  • 1
    `toFixed(99999999999999.9999,4)` returns `100000000000000` – FaizanHussainRabbani Aug 30 '21 at 19:10
  • That is a js problem. Floating numbers in js can only handle up to 64 bits. If you do `console.log(99999999999999.9999)` it will print rounded int as well. Actually, if you declare a variable like that it will be rounded int. I recommend you this read: https://www.avioconsulting.com/blog/overcoming-javascript-numeric-precision-issues – Santiago Hernández Apr 28 '22 at 13:58
5

Additionally, to prevent toFixed() from rounding off decimal numbers and to make your number into two decimal places you can use this,

val = (Math.floor(100 * val) / 100).toFixed(2);
Tomasz Kowalczyk
  • 10,472
  • 6
  • 52
  • 68
Quiams
  • 59
  • 1
  • 3
1

if you want to avoid rounding off... Ex. 1.669 => 1.67, 548.466 => 548.47

The function result looks like: 1.669 => 1.66, 548.466 => 548.46

Then the following JQuery function will help you. It is tested and working properly.

<input name="a" type="text" id="a" size="7%" tabindex="2">




  $('#a').keyup(function(e){
        if($(this).val().indexOf('.')!=-1){ 
            if($(this).val()=="." && $(this).val().length==1){
                this.value = parseFloat(0).toFixed(1);
            }else if($(this).val().split(".")[1].length > 2){                
                if( isNaN( parseFloat( this.value ) ) ) 
                    return;
                  this.value = $(this).val().split(".")[0]+"."+$(this).val().split(".")[1].substring(0,2);
            }   
        }
   });
Sachin Parse
  • 1,269
  • 11
  • 12
0

This may sound really dumb, but why not prevent rounding mathematically?

Basically what you are doing is displacing the value by half a precision point, in this way the rounding becomes floored.

Normal: 0 to 4 make a 0 5 to 9 make a 10

After you substract half a precision point: -4 (becomes 6) to -1 (becomes 9) make a 0 0 to 4 make a 0

You would need to substract 0.5/10^N In this case for 2 decimals 0.5/10^2 = 0.5/100 = 0.005 for 4 decimals you would need to substract 0.00005

But, because of the way that floating point works in computers and in the cpu you need to make sure you have enough precision to be used for this. So I would strongly suggest you remove the precision to a point that the cpu can't mess it up.

num = 123456.786
Math.trunc(num) + (num-Math.trunc(num)-0.005).toFixed(2)

num = 123456.781
Math.trunc(num) + (num-Math.trunc(num)-0.005).toFixed(2)

num = 123456.789
Math.trunc(num) + (num-Math.trunc(num)-0.005).toFixed(2)

0

This is tested and works for all cases.

function truncateTwoDecimals(num, dec = 2) {
  const totalDecimal = countDecimals(num);
  if (totalDecimal === 1) {
    dec = 1;
  }
  if (totalDecimal === 2) {
    return num;
  }
  const calcDec = Math.pow(10, dec);
  return Math.trunc(num * calcDec) / calcDec;
}
function countDecimals(value) {
  if(Math.floor(value) === value) return 0;
  return value.toString().split(".")[1].length || 0; 
}
const res = truncateTwoDecimals(1.13);
console.log(res);
console.log(truncateTwoDecimals(4492903605.9));
anonymous
  • 1,499
  • 2
  • 18
  • 39