4

I'm working on a "toy problem" where I am supposed to write a JavaScript function that converts a decimal into a fraction and returns it as a string. For example: fractionConverter(2.75) should return "11/4".

Here is my code:

function fractionConverter (number) {
  if (number > 0) {
    var isNegative = false;
  } else if (number < 0) {
    var isNegative = true;
  }

  number = Math.abs(number);

  if (number % 1 === 0) {
    var finalFrac = number + "/1";
  } else {
    for (var i = 2; i < 10000000000; i++) {
      if ((i * number) % 1 === 0) {
        var finalFrac = (i * number) + "/" + i;
      }
      if (finalFrac) { break; }
    }
  }

  var getFrac = function(numString, bool) {
    if (!bool) {
      return numString;
    } else {
       return "-" + numString;
    }
  }

  return getFrac(finalFrac, isNegative);
}

Sorry about the formatting. Anyway, I'm getting a weird spec failure. The function returns the correct values for the following numbers: 0.5, 3, 2.5, 2.75, -1.75 and .88. For some reason, however, it is failing on 0.253213. It is returning 1266065/5000000 instead of 253213/1000000. Not really sure why.

Thanks

sarath
  • 343
  • 8
  • 31
user3143105
  • 129
  • 1
  • 12
  • 2
    That's because `0.253213 * 1000000 === 253213.00000000003`. See [Is floating point math broken?](http://stackoverflow.com/q/588004/1529630) – Oriol Jan 29 '16 at 04:59
  • 1
    Not only is it impossible to represent all fractions accurately using decimals, ECMAScript's [*number* values](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-terms-and-definitions-number-value) can't accurately represent all decimal numbers either. – RobG Jan 29 '16 at 04:59
  • OK, I am voting to reopen this question. I want to know the best way to approach this particular problem, given the limitations of floating point numbers. – Andrew Shepherd Jan 29 '16 at 05:02
  • @AndrewShepherd—Ok, you have a point. There's gotta be a better way than a 1.0e10 loop. ;-) – RobG Jan 29 '16 at 05:05

5 Answers5

3

I am just improving @william's answer, I think this script gives you more reduced fraction.

function fractionConverter(number) {
  var fraction = number - Math.floor(number);
  var precision = Math.pow(10, /\d*$/.exec(new String(number))[0].length);
  var getGreatestCommonDivisor = function(fraction, precision) {
    if (!precision)
      return fraction;
    return getGreatestCommonDivisor(precision, fraction % precision);
  }
  var greatestCommonDivisor = getGreatestCommonDivisor(Math.round(fraction * precision), precision);
  var denominator = precision / getGreatestCommonDivisor(Math.round(fraction * precision), precision);
  var numerator = Math.round(fraction * precision) / greatestCommonDivisor;

  function reduce (numer,denom) {
    for (var i = 2; i >= 9; i++) {
      if ((numer%i===0) && (denom%i)===0) {
        numerator=numer/i;
        denominator=denom/i;
        reduce(numerator,denominator);
      };
    };
  }
reduce(numerator,denominator);
  return numerator + "/" + denominator;
}

document.getElementById("output").innerHTML = fractionConverter(0.24888);

Here is the HTML

<body>
  <p id="output"></p>
</body>

</html>
sarath
  • 343
  • 8
  • 31
2

Javascript doesn't deal with floating point numbers accurately.

I tried typing this into node:

0.253213 * 1000000

And I got this:

253213.00000000003

Here is a different approach to testing for a multiplier

var bigNumber = Math.pow(10,8);

var isDivisible = (Math.round(i * number * bigNumber)/bigNumber % 1) == 0;

This will help you some of the way.

This also work the way you might expect it to, if you wanted 0.333333333 to be treated as 1/3.

One issue is that the highest integer you can have is javascript is between 10^15 and 10^16.

If ((number * bigNumber) > 2^53) this will not work.

Andrew Shepherd
  • 44,254
  • 30
  • 139
  • 205
  • Thank you. The isDivisible thing worked although I'm not sure I understand why. We create a giant number and then times it by the number and i and then round that number because, for some reason I don't understand, JavaScript is going to return a decimal instead of an integer. Then we cancel out our big number by dividing our rounded value by bigNumber and check to see if *that* value is divisible by 1. What a mess. – user3143105 Jan 29 '16 at 05:49
1

I completely changed the structure of your code, but this solution does work. It is based off of code from this thread. I hope this helps.

function fractionConverter(number) {
  var fraction = number - Math.floor(number);
  var precision = Math.pow(10, /\d*$/.exec(new String(number))[0].length);
  var getGreatestCommonDivisor = function(fraction, precision) {
    if (!precision)
      return fraction;
    return getGreatestCommonDivisor(precision, fraction % precision);
  }
  var greatestCommonDivisor = getGreatestCommonDivisor(Math.round(fraction * precision), precision);
  var denominator = precision / greatestCommonDivisor;
  var numerator = Math.round(fraction * precision) / greatestCommonDivisor;

  return numerator + "/" + denominator;
}

document.getElementById("output").innerHTML = fractionConverter(0.253213);
<!DOCTYPE html>
<html>

<body>
  <p id="output"></p>
</body>

</html>
Community
  • 1
  • 1
William Callahan
  • 630
  • 7
  • 20
1

The caveat to this answer is that ECMAscript inadequately handles Decimals. Also, note that the following is largely pseudocode, but should work with minor fixes.

Here is a javascript solution to this problem:

var decimal_to_fraction = {
  "numerator": 0,
  "denominator": 0,
  "simplified_numerator": this.numerator,
  "simplified_denominator": this.denominator,
  "init": function(numerator, denominator){
    this.numerator = numerator
    this.denominator = denominator
  },
  "get_divisor": function(numerator, denominator){
    var divisor = 0;
    var divisors = [1, 2, 3, 4, 5];

    for (i in divisors) {
      if (!(numerator % divisor) && !(denominator % divisor)) {
        divisor = i;
        break
      }
    }

    return divisor
  },
  "calculate_fraction": function() {
    var simplified = false;
    divisor = this.get_divisor(numerator_denominator);

    if (divisor) {
      while (simplified == false) {
        if (this.simplfieid_numerator / divisor and this.simplified_denominator / divisor) {
          this.simplified_numerator = simplified_numerator / divisor
          this.simplified_denominator = simplified_denominator / divisor
        } else {
          simplified = true
        }
      }
    }

    return (this.simplified_numerator, this.simplfieid_denominator)
  },
  "get_fraction": function() {
    this.calculate_fraction()

    fraction = "{0} / {1}".format(this.simplfieid_numerator, this.simplified_denominator"

    return fraction
  }

}

decimal_to_fraction.get_fraction()

In case you were curious, here's a Python solution to your problem:

class DecimalToFraction(object):
    def __init__(decimal):
        self.numerator = decimal * 100
        self.denominator = 100
        self.simplified_numerator = self.numerator
        self.simplified_denominator = self.denominator

    def get_divisor(self, numerator, denominator):
        divisor = 0

        for i in range(0,5):
            if not numerator % divisor and not denominator % divisor:
                divisor = i
                break

        return divisor

    def calculate_fraction(self):
        simplified = False

        divisor = get_divisor(self.numerator, self.denominator)

        if divisor:
            while simplified == False:
                if self.simplified_numerator / divisor and self.simplfieid_denominator / divisor:
                    self.simplified_numerator = simplified_numerator / divisor
                    self.simplified_denominator = simplified_denominator / divisor
                else:
                    simplified = True

        return (self.simplified_numerator, self.simplified_denominator)


    def get_fraction(self):
        self.calculate_fraction()

        fraction = "{0} / {1}".format(self.simplified_numerator, self.simplified_denominator)

        return fraction


#d2f = DecimalToFraction(<decimal>)
#d2f.get_fraction()
pygeek
  • 7,356
  • 1
  • 20
  • 41
0

You can use Erik Garrison's fraction.js library to do that and more fractional operations.

To to do 1.75 , you can just do

var f = new Fraction(1.75);
console.log(f.toFraction()); // Results "1 3/4" 
console.log(f.s * f.n + " / " + f.d); // Results "7 / 4"
console.log(f.toString()); // Results "1.75
Abhijeet
  • 1,515
  • 1
  • 11
  • 21