1

I have developed the following short function to split a number passed to it in a string format into its Whole and Fractional Parts (also outputted as strings).

Note1: The Whole part of the number can run into large numbers (in excess of 50 digits).

Note 2: The output result (the Whole and the Fraction strings) will not be converted into an integer or a float but will be further manipulated only as a string due to the Javascript rounding for large numbers. So going back to numbers is not a concern here

Because the function detects the machine's locale automatically, it is therefore expected that the user enters (passes) the number in the locale of his local machine or otherwise the number is generated programmatically and passed to the function.

The number is expected to be passed as a "string" due to the very large length and also because there is no handling for numbers passed in exponent (e) format.

The function uses the toLocaleString() to detect the decimal and thousand separators.

I have tested the function with the major number systems (.,' space) and so far, so good.

The question is, how safe will this code be, and are there any alternative better and safer methods to do it or corrections/changes necessary?

Thanks

 function splitFloatString(NumString) {
    var decimalSeparator  = (1.1).toLocaleString().substring(1,2);  // Get Decimal Separator
    var thousandSeparator = (1000).toLocaleString().substring(1,2); // Get Thousands Separator
    NumString += "";                                                // ensure a string
    var fraction ="0";                                              // default no fractional part
    NumString = NumString.replace(RegExp("\\"+thousandSeparator,"g"),"");   //remove thousand separators if any

    if (RegExp("\\"+decimalSeparator,"g").test(NumString)) {        // test for decimal separator
       var n = NumString.split(decimalSeparator);                   // Split at Decimal Seprator
       NumString = n[0];                                            // The Whole part
       if (n.length==2) fraction = n[1];                            // The Fractional part
       if (fraction=="") fraction ="0";
       }

  console.log("Whole: ("+NumString+"), Fraction: ("+fraction+")");  // added for testing
  //return n=[NumString,fraction];                                  // normal return uncomment
}

 //------------------------------------------------------------------------------------
 // Tests assuming user's machine and user enters/passes US-EN separators as an example
 //------------------------------------------------------------------------------------
        splitFloatString("123456789123456699999887788812340786.45678907656912574321115194123123456789");
        splitFloatString("1,234,567,891,234,566,999,998,888,812,340.456520754186789075194123123456789");
        splitFloatString("200")
        splitFloatString("0.")
        splitFloatString(123456.2349999)
        splitFloatString("")
        splitFloatString()
Mohsen Alyafei
  • 4,765
  • 3
  • 30
  • 42
  • From what environment are the inputs coming from? If from a JavaScript as well, I would suggest you encounter for exponential numbers `3.51e+41` for example. – Ahmed Hammad May 30 '20 at 18:40
  • @AhmedHammad Agree. But this would need adding code to convert exponent numbers to full string equivalents. Which is possible to do. Thanks – Mohsen Alyafei May 30 '20 at 18:47

4 Answers4

2

According to me, you are just complicating the whole thing unnecessarily. Here's a simple way to achieve the same result.

function getLocaleSeparators() {
  const testNumber = 1000.1.toLocaleString()
  return [testNumber.substring(1,2), testNumber.substring(5,6)]
}

function splitFloatString(number) {
  const numberString = number.toString()
  const [thousandSeparator, decimalSeparator] = getLocaleSeparators()
  let [wholePart, fractionPart] = numberString.replace(new RegExp(thousandSeparator, 'g'), '').split(decimalSeparator)
  wholePart = wholePart || "0"
  fractionPart = fractionPart || "0"
  console.log(`Whole: ${wholePart}, Fraction: ${fractionPart}`);
}

function getLocaleSeparators() {
  const testNumber = 1000.1.toLocaleString()
  return [testNumber.substring(1,2), testNumber.substring(5,6)]
}

function splitFloatString(number) {
  const numberString = number.toString()
  const [thousandSeparator, decimalSeparator] = getLocaleSeparators()
  let [wholePart, fractionPart] = numberString.replace(new RegExp(thousandSeparator, 'g'), '').split(decimalSeparator)
  wholePart = wholePart || "0"
  fractionPart = fractionPart || "0"
  console.log(`Whole: ${wholePart}, Fraction: ${fractionPart}`);
}

splitFloatString("123456789123456699999887788812340786.45678907656912574321115194123123456789");
splitFloatString("1,234,567,891,234,566,999,998,888,812,340.456520754186789075194123123456789");
splitFloatString("200")
splitFloatString("0.")
splitFloatString(123456.2349999)
splitFloatString("")
rishichawda
  • 423
  • 4
  • 11
  • But you are assuming here that a comma and the dot are the separators "always". – Mohsen Alyafei May 30 '20 at 18:45
  • 1
    @MohsenAlyafei Updated the answer. – rishichawda May 30 '20 at 18:58
  • thanks. The locale string detection is done in one instruction. – Mohsen Alyafei May 30 '20 at 19:10
  • Passing this to the function `splitFloatString()` will break it with an error. – Mohsen Alyafei May 30 '20 at 19:20
  • Why would you even want to do that? Parameters should either be with a default value if optional or always passed to the function when it expects one. Otherwise, you are doing something pretty wrong with the codebase in general. And it will leave a lot of opportunities for bugs to creep in. But if you still want, you can add a check on the start of the function and return accordingly, but I wouldn't suggest that. – rishichawda May 30 '20 at 19:26
  • 1
    The best thing you could do is, set the parameter to default as `""`, which will return both as 0. Basically, `function splitFloatString(number = "")` on the definition. – rishichawda May 30 '20 at 19:28
  • The function could be called from other codes with `undefined` numbers. Necessary to capture such cases. – Mohsen Alyafei May 30 '20 at 19:32
  • 1
    The current solution is handling all the cases. Ergo, would be the safest piece to use then. – rishichawda May 30 '20 at 19:35
1

I recommend you to use the math.floor function for such purposes.

It rounds the number passed as parameter to its nearest integer in downward direction:

Also, The Math.ceil() function rounds a number up to the next largest whole number or integer.

For other handy options, you can check out this https://www.w3docs.com/snippets/javascript/how-to-convert-a-float-number-to-whole-number-in-javascript.html

Bùi Đức Khánh
  • 3,975
  • 6
  • 27
  • 43
Nancy Brown
  • 360
  • 3
  • 2
0

Please try with this one. might help you.

number = "21212.32323";
var numberString = number + ''; // converts into string
var index = numberString.lastIndexOf("."); // get the index
var strFloat, wholeStr;
if(index === -1) {
  strFloat = '';
  wholeStr = numberString.replace(/[^\w\s]/gi, '').replace(/ /g,'')
} else {
 strFloat = numberString.substring(index + 1); // get the decimal part
 var strWhole = numberString.substring(0, index); // get the number
 wholeStr = strWhole.replace(/[^\w\s]/gi, '').replace(/ /g,'') // remove spcl character
}

console.log(`Whole: ${wholeStr}, Fraction: ${strFloat}`);

And yes you can not exceed MAX_SAFE_INTEGER in javascript i.e 32-bits. What is JavaScript's highest integer value that a number can go to without losing precision?

sibabrat swain
  • 1,277
  • 8
  • 20
0

Have been able to finally get the final answer I was looking for.

Thanks to all for their assistance and ideas:

function numberSplit (NumString) {
  var decimalSep  = 1.1.toLocaleString().substring(1,2),  // Get Deciaml Separator
      thousandSep = 1e3.toLocaleString().substring(1,2),  // Get Thousand Separator
      fraction    = "0",                                  // Default "0"
      n = (NumString = (NumString +="").replace(RegExp("\\"+thousandSep,"g"),"")).split(decimalSep);
  NumString = n[0] ;                              // Get Whole Part
  NumString == "" && (NumString = undefined);     // Whole = undefined if empty
  n.length  == 2  && (fraction  = n[1]);          // Get Fractional part (only if 1 decimal place)
  fraction  == "" && (fraction  = "0");           // Fraction = 0 if empty

console.log("Whole: ("+NumString+"), Fraction: ("+fraction+")")
}
  
//-----------------------------------------------------------
// Ttests assuming user enters US-EN separators as an example
//-----------------------------------------------------------
numberSplit("123456789123456699999887788812340786.45678907656912574321115194123123456789");
numberSplit("1,234,567,891,234,566,999,998,888,812,340.456520754186789075194123123456789");
numberSplit("200")
numberSplit("0.")
numberSplit(123456.2349999)
numberSplit("1.2.3");           // Fractional part ignored as invalid
numberSplit("")                 // Return undefined
numberSplit()                   // Return undefined
numberSplit(NaN)                // Return NaN
numberSplit(undefined)          // Return undefined
Mohsen Alyafei
  • 4,765
  • 3
  • 30
  • 42