2

Given any number between 0 and 1, such as 0.84729347293923, is there a simple way to make it into 84729347293923 without string or regex manipulation? I can think of using a loop, which probably is no worse than using a string because it is O(n) with n being the number of digits. But is there a better way?

function getRandom() {
  let r = Math.random();
  while (Math.floor(r) !== r) r *= 10;
  return r;
}

for (let i = 0; i < 10; i++)
  console.log(getRandom());
nonopolarity
  • 146,324
  • 131
  • 460
  • 740
  • 1
    The number you provide is *not* an integer. Integers are a set of whole numbers and zero. You could either round it or floor it. There are no other options in mathematics, – GetSet Feb 09 '20 at 00:04
  • Any particular reason you want to avoid string manipulation? Slicing off the '0.' is really simple – lucasvw Feb 09 '20 at 00:05
  • Or can you use the length of the string at least? – lucasvw Feb 09 '20 at 00:06
  • If the number will be always less than 1, treat it as a percentage and multiply it by 100. Then trunc or round it. Even if its not less than 1, same rule would apply. – GetSet Feb 09 '20 at 00:25
  • @GetSet That gets you 84, not 84729347293923. – ray Feb 09 '20 at 00:41
  • i guess if you want to leave O(n) you have to include a distribution. do you have knowlwgde of the avarage length and thus can assume *10000000 is correct 60%? if you cant make assumptions to the length you kinda have to count its length - binary search? MAX_SAFE_INTEGER has a length of 16, so assume a length of 8 – Estradiaz Feb 09 '20 at 08:35
  • Possibly related: https://stackoverflow.com/questions/4414077/read-write-bytes-of-float-in-js – chiliNUT Feb 09 '20 at 20:27

3 Answers3

3

Integers mod 1 = 0, non integers mod 1 != 0.

while ((r*=10) % 1);
chiliNUT
  • 18,989
  • 14
  • 66
  • 106
  • 1
    it makes me recall one of the simplest way to copy a string in C was `while(*p++ = *q++);` – nonopolarity Feb 09 '20 at 19:39
  • 1
    so your line could even just be `while ((r*=10)%1);`... but it is essentially the same as my method... I wonder if there is a `O(1)` method – nonopolarity Feb 09 '20 at 21:33
  • 1
    I think this is the best method to convert the float part of a number between 0 and 1 without using regex or string methods in an integer, i think there aren't any other best ways to do this in javascript. – Layer Feb 09 '20 at 21:40
  • @nonopolarity yeah I'd agree that it's basically the same as urs – chiliNUT Feb 09 '20 at 22:36
2

Ok, just want to refactor my code (i realized that was bad so this is what i discovered to correctly get the value as you requested).

NOTE: As the question says that "given any number between 0 and 1", this solution only works for values between 0 and 1:

window.onload = ()=>{

    function getLen(num){
        
        let currentNumb = num;
        let integratedArray = [];
        let realLen = 0;

        /*While the number is not an integer, we will multiply the copy of the original
         *value by ten, and when the loop detects that the number is already an integer
         *the while simply breaks, in this process we are storing each transformations
         *of the number in an array called integratedArray*/
        while(!(Number.isInteger(currentNumb))){
            currentNumb *= 10;
            integratedArray.push(currentNumb);
        }

        /*We iterate over the array and compare each value of the array with an operation
         *in which the resultant value should be exactly the same as the actual item of the
         *array, in the case that both are equal we assign the var realLen to i, and
         *in case that the values were not the same, we simply breaks the loop, if the
         *values are not the same, this indicates that we found the "trash numbers", so
         *we simply skip them.*/
        for(let i = 0; i < integratedArray.length; i++){

            if(Math.floor(integratedArray[i]) === Math.floor(num * Math.pow(10, i + 1))){
                realLen = i;
            }else{
                break;
            }

        }

        return realLen;

    }

    //Get the float value of a number between 0 and 1 as an integer.
    function getShiftedNumber(num){

        //First we need the length to get the float part of the number as an integer
        const len = getLen(num);
        /*Once we have the length of the number we simply multiply the number by
         *(10) ^ numberLength, this eliminates the comma (,), or point (.), and
         *automatically transforms the number to an integer in this case a large integer*/
        return num * (Math.pow(10, len));

    }

    console.log(getShiftedNumber(0.84729347293923));

}

So the explanation is the next:

Because we want to convert this number without using any string, regex or any another thing, first we need to get the length of the number, this is a bit hard to do without using string conversions... so i did the function getLen for this purpose.

In the function getLen, we have 3 variables:

  • currentNumb: This var is a copy of the original value (the original number), this value help us to found the length of the number and we can do some transforms to this value whitout changing the original reference of the number.

We need to multiply this value any times is needed to transform the number to an integer and then multiplyng this value by ten to ten. with the help of a while (this method makes the number a false integer).

NOTE: I saw "False integer" because when i was making the tests i realized that in the number is being adding more digits than normal... (Very very strange), so this stupid but important thing makes neccesary the filter of these "trash numbers", so later we proccess them.

  • integratedArray: This array stores the values of the result of the first while operations, so the last number stored in this array is an integer, but this number is one of the "fake integers", so with this array we need to iterate later to compare what of those stored values are different to the original value multiplied by (10 * i + 1), so here is the hint:

In this case the first 12 values of this array are exactly the same with the operation of Math.floor(num * Math.pow(10, i + 1))), but in the 13th value of the array these values are not the same so... yes!, there are those "trash numbers" that we were searching for.

  • realLen: This is the variable where we will store the real length of the number converting the float part of this number in an integer.
Layer
  • 326
  • 3
  • 12
  • “any number between 0 and 1” – ray Feb 09 '20 at 00:38
  • @Riven how do you get `len` when `num` given is `0.84729347293923` – nonopolarity Feb 09 '20 at 03:55
  • @nonopolarity code corrected, answer is a bit hard, is explained in my code but... to make the calculation of the length we need first to convert the number to an integer or directly count the digits that are after the comma or a point, this is suposed to be the length of the float part of this number already converted in an integer, so this is how i made the calculation to make the conversion whitout regex or string manipulations, in this case i helped me by using the comma (count the digits before the number being transformed in an integer by increasing a var), this is a simple operation. – Layer Feb 09 '20 at 07:26
0

Some binary search approach:

Its useless if avarage length < 8;

It contains floating point issues.

But hey it is O(log n) with tons of wasted side computations - i guess if one counts them its event worse than just plain multiplication.

I prefer @chiliNUT answer. One line stamp.

function floatToIntBinarySearch(number){

   const max_safe_int_length = 16;
   const powers = [
                    1,
                    10,
                    100,
                    1000,
                    10000,
                    100000,
                    1000000,
                    10000000,
                    100000000,
                    1000000000,
                    10000000000,
                    100000000000,
                    1000000000000,
                    10000000000000,
                    100000000000000,
                    1000000000000000,
                    10000000000000000
                  ]
    let currentLength = 16
    let step = 16
    
    let _number = number * powers[currentLength]
    
    while(_number % 1 != 0 || (_number % 10 | 0) == 0){
       
       step /= 2 
       if( (_number % 10 | 0) == 0 && !(_number % 1 != 0)){
         
         currentLength =  currentLength - step;
       } else {
         
         currentLength = step + currentLength;
       }
       if(currentLength < 1 || currentLength > max_safe_int_length * 2) throw Error("length is weird: " + currentLength)
       
       _number = number * powers[currentLength]
       console.log(currentLength, _number)
       if(Number.isNaN(_number)) throw Error("isNaN: " + ((number + "").length - 2) + " maybe greater than 16?")
    }
    return number * powers[currentLength]
}
let randomPower = 10 ** (Math.random() * 10 | 0)
let test = (Math.random() * randomPower | 0) / randomPower
console.log(test)
console.log(floatToIntBinarySearch(test))
Community
  • 1
  • 1
Estradiaz
  • 3,483
  • 1
  • 11
  • 22