0

I'm trying to get a number to the nth decimal place without rounding off
The closest I could find was from this source

num = num.toString(); //If it's not already a String
num = num.slice(0, (num.indexOf("."))+3); //With 3 exposing the hundredths place
Number(num); //If you need it back as a Number

but it has it's limitations

this is what I'm trying to achieve:

if n=3
16            -> 16.000
16.000001     -> 16.000
16.12345      -> 16.123
4239.20902190 -> 4239.209

I'm trying to stay way from a mathematical approach and rather use a string approach as mathematical approaches sometimes become unpredictable, so is there any modern way of achieving the desired results?

I may have put an incorrect title to the problem so any edit is welcome

  • All numbers (except BigInt) are 32bit float values in JS. `16 === 16.000` – evolutionxbox May 24 '21 at 09:56
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed . Notice, that `toFixed` returns a string, JS has [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) floating point numbers only (and [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)), having a number with zero decimals is not possible. – Teemu May 24 '21 at 09:58

3 Answers3

0

If you multiply a number by 10, then use Math.floor to remove everything after the decimal place, THEN divide by 10, you get the value of the original number to one decimal place with no rounding. If instead of 10 you use 100, it'll be 2 decimal places. 1000 makes 3, 10000-4, etc.

Then using number.prototype.ToFixed(n), we can get a string out that will always have n decimal places.

Combining these together you get something like:

function toDecimalPlaceWithoutRounding(number, precision) {
  const factor = 10 ** precision; // "x ** y" means x to the power of y
  const value = Math.floor(number * factor) / factor;
  return value.toFixed(precision);
}

a quick test of this:

function toDecimalPlaceWithoutRounding(number, precision) {
  const factor = 10 ** precision;
  const value = Math.floor(number * factor) / factor;
  return value.toFixed(precision);
}

for (let i = 0; i < 10; i++) {
  const number = Math.random() * 20;
  const result = toDecimalPlaceWithoutRounding(number, 3);
  
  console.log(number,result);
}

NOTE you could just use .toFixed, but it will round. eg. (3.555).toFixed(2) will give "3.56".


EDIT negative support:

    function toDecimalPlaceWithoutRounding(number, precision) {
      const factor = 10 ** precision;
      const roundFunc = number > 0 ? Math.floor : Math.ceil; // use floor for positive numbers, ceil for negative
      const value = roundFunc(number * factor) / factor;
      return value.toFixed(precision);
    }

    for (let i = 0; i < 10; i++) {
      const number = Math.random() * 20 - 10;
      const result = toDecimalPlaceWithoutRounding(number, 3);
      
      console.log(number,result);
    }
  • yea I was trying to avoid toFixed() because of the rounding off problem , althought your code is lovely and it works nicely it still has a flaw for negative numbers eg- `-1.99999 -2.000` are there any other flaws? –  May 24 '21 at 10:26
  • @cakelover other flaws would be if either of the values aren't provided, or if either value isn't a number, will likely return `NaN` or throw an error. I can add support for negative if you like? It will add another line of code to the function though. –  May 24 '21 at 10:30
  • Will also likely not perform predictably with really large numbers –  May 24 '21 at 10:32
  • 1
    Actually based on your basic logic I came up with this https://jsfiddle.net/mpg1buxn/ can you find some possible flaws in this (given all inputs are always numbers) ? if you'd like you can edit and add this to your answer since it's based on your idea –  May 24 '21 at 10:36
  • 1
    @cakelover, I didn't know about `Math.trunc` that's way better. You should make your own answer, I'll give you an upvote and you should be able to accept it as the correct answer in a couple of days. –  May 24 '21 at 10:38
0

This can be achieved by 10**n and Math.round()

function roundWithPrecision(num,n){ 
    //return Math.round(num * 10 ** n)/(10**n)
    return Math.floor(num * 10 ** n)/(10**n)
  }

A test of the function:

a=roundWithPrecision(16.12345,4)
16.1234
b=roundWithPrecision(16.123456,5)
16.12346
Aaron Chen
  • 136
  • 9
  • `roundWithPrecision(1.9999,3)` gives 2 instead of 1.999 –  May 24 '21 at 11:01
  • @cakelover https://stackoverflow.com/questions/1458633/how-to-deal-with-floating-point-number-precision-in-javascript – evolutionxbox May 24 '21 at 11:16
  • @cakelover just saw your anwser, glad to see that Math.trunc suits your need, just wonder maybe Math.floor may also work for your requirement too? – Aaron Chen May 24 '21 at 11:33
0

So I found the solution thanks to @Callum Morrisson
by using Math.trunc():

function toPrecision(num,precision)
{
  num=Math.trunc(num*10**precision)/10**precision;
  return num;
}

console.log(toPrecision(-21873212130.119281231231231,4))

jsfiddle here

  • need the `.toFixed(precision)` to get your string out though, if you just return the number then `toPrecision(16, 3)` will give you `16` instead of `"16.000"`. –  May 24 '21 at 10:44
  • @CallumMorrisson that gives an error like this https://jsfiddle.net/u16twy9k/ –  May 24 '21 at 10:53
  • @CallumMorrisson based on that I'm getting this error : https://jsfiddle.net/zw7ng8um/ –  May 24 '21 at 10:58
  • 1
    Actually yeah sorry, I am seeing the error, with larger numbers float precision gets weird. So there's limitations –  May 24 '21 at 11:02
  • @CallumMorrisson let me know if you come across a reliable solution :) –  May 24 '21 at 11:03