3

I would love to be able to move a decimal point 2 places across an unknown amount of numbers without using math. I know that seems weird, but finite precision causes some shifts. My javascript is not strong, but I'd really like to learn how to chop up a number and do this if it's possible. So, I'm hoping you awesome folks can help.

The problem:

  • 575/960 = 0.5989583333333334 using the console
  • I would like to make that a copy and pastable percentage like: 59.89583333333334%
  • If I use math and multiply by 100, it returns 59.895833333333336 because of finite precision

Is there a way to make that a string and just always move the decimal 2 places to the right to skip the math?

Here's a fiddle too, with the codes: http://jsfiddle.net/dandenney/W9fXz/

If you want to know why I need it and want the precision, it's for this little tool that I made for getting responsive percentages without using the calculator: http://responsv.com/flexible-math

Dan Denney
  • 290
  • 2
  • 14
  • Don't forget you're working with web browsers with displays that are probably smaller than 4K x 4K pixels. Probably five or six digits of precision are sufficient for any calculation, right? – sarnold Apr 20 '12 at 02:22
  • Have you considered `toFixed`? e.g. `(575 / 960).toFixed(4)` would be 0.5989 (as a string). That might be easier to work with (not to mention prettier). – Peter C Apr 20 '12 at 02:23
  • Why skip the math when its really simple? You could easily just use divide by 100 to jump it 2 spaces to left every time `var numb = 0.123; var newNumb = numb/100`. I mean, is that really that hard? – SpYk3HH Apr 20 '12 at 02:28
  • if it's a string you just use parseFloat on it `newNumb = parseFloat(numb)/100;` That's not really any math at all – SpYk3HH Apr 20 '12 at 02:30
  • 1
    @SpYk3HH I would've normally just skipped over this but you threw in some bonus condescension with the "I mean, is that really that hard?". I felt dumb for a second but then read that you blasted me without even really reading the problem. Dividing would go the opposite direction and multiplying suffers from the problem with finite precision that I mentioned. So, var numb = 575/960; newNumb = parseFloat(numb)*100; console.log(newNumb); > 59.895833333333336 I am not strong with JS and tried to explain my reasoning. I'd suggest skipping questions from noobs if you don't have the patience. – Dan Denney Apr 20 '12 at 02:58
  • now it says to the right, but your statement originally was `the decimal 2 places to the left`, thus division would've been correct, and you mistake condesension for simple online sarcasm. I was just wondering why it was so hard to use the basics? There's an old saying, "Why go around your a!! to get to your elbows?" – SpYk3HH Apr 20 '12 at 03:02

3 Answers3

4

If the original number is of this type of known structure and always has at least two digits to the right of the decimal, you can do it like this:

function makePercentStr(num) {
    var numStr = num + "";
    // if no decimal point, add .00 on end
    if (numStr.indexOf(".") == -1) {
        numStr += ".00";
    } else {
        // make sure there's at least two chars after decimal point
        while (!numStr.match(/\.../)) {
            numStr += "0";        
        }
    }
    return(numStr.replace(/\.(..)/, "$1.")
           .replace(/^0+/, "")    // trim leading zeroes
           .replace(/\.$/, "")    // trim trailing decimals
           .replace(/^$/, "0")    // if empty, add back a single 0
           + "%");
}

Working demo with test cases: http://jsfiddle.net/jfriend00/ZRNuw/

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1
    but it won't work for 4/2. because "2" becomes 2 instead of 200%. – Ray Cheng Apr 20 '12 at 02:27
  • Thank you, this definitely works! For the way that I was doing it I had to convert it to a string first, but then BOOM! – Dan Denney Apr 20 '12 at 02:28
  • Like I said right in the first sentence of my answer - it works for numbers that have two digits to the right of the decimal like the one they asked about. If it needs to work for all possible input numbers, then it can be made smarter. – jfriend00 Apr 20 '12 at 02:29
  • @DanDenney - OK, I've modified the answer to work with any input numbers with or without decimals, with or without enough digits to the right of the decimal. – jfriend00 Apr 20 '12 at 02:37
  • @jfriend00 That's awesome and it may help people that stumble across this when they search. Your original answer was perfect for what I needed though. Thank you so much! – Dan Denney Apr 20 '12 at 02:47
1

The question asks to solve the problem without Math but the below solution involves math. I am leaving it for just a reference

function convertToPercentage(num) {
    //Changes the answer to string for checking
    //the number of decimal places.
    var numString = num + '';
    var length = (numString).substring(numString.indexOf(".")+1).length;

    //if the original decimal places is less then
    //no need to display decimals as we are multiplying by 100
    //else remove two decimals from the result
    var precision = (length < 2 ? 0 : length-2);

    //if the number never contained a decimal. 
    //Don't display decimal.
    if(numString.indexOf(".") === -1) {
         precision = 0;   
    }        
    return (num * 100).toFixed(precision) + "%";
}        

Working jsFiddle here with same test cases as the accepted answer.

Community
  • 1
  • 1
Ramesh
  • 13,043
  • 3
  • 52
  • 88
0

I've used this method because of the risk of floating errors:

const DECIMAL_SEP = '.';

function toPercent(num) {
  const [integer, decimal] = String(num).split(DECIMAL_SEP);

  // no decimal, just multiply by 100
  if(typeof decimal === 'undefined') {
    return num * 100;
  }

  const length = decimal.length;

  if(length === 1) {
    return Number(integer + decimal + '0');
  }

  if(length === 2) {
    return Number(integer + decimal);
  }

  // more than 2 decimals, we shift the decimal separator by 2
  return Number(integer + decimal.substr(0, 2) + DECIMAL_SEP + decimal.substr(2));
}

console.log(toPercent(10));
console.log(toPercent(1));
console.log(toPercent(0));
console.log(toPercent(0.01));
console.log(toPercent(0.1));
console.log(toPercent(0.12));
console.log(toPercent(0.123));
console.log(toPercent(12.3456));
Olivier
  • 1,270
  • 1
  • 10
  • 24