Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to identify periods in decimals of bignumbers #3176

Open
nycos62 opened this issue Mar 16, 2024 · 5 comments
Open

how to identify periods in decimals of bignumbers #3176

nycos62 opened this issue Mar 16, 2024 · 5 comments
Labels

Comments

@nycos62
Copy link

nycos62 commented Mar 16, 2024

Hello, maybe a stupid question, but is there a natural way to get periodicity in BigNumbers decimals

math.config({
  number: 'BigNumber',      // Default type of number:
                            // 'number' (default), 'BigNumber', or 'Fraction'
  precision: 20             // Number of significant digits for BigNumbers
}) 
math.divide(math.bignumber(1),math.bignumber(6)).toFixed();
'0.16666666666666666667'

as you can see it ends with digit '7'

I wish to identify here a period of length 1 starting at decimal 2 :
'0.1 ͞6'

thx

@josdejong
Copy link
Owner

josdejong commented Mar 16, 2024

You can use fractions for that, like:"

math.fraction(1, 6).toString() // "0.1(6)"
math.fraction(1, 7).toString() // "0.(142857)"

EDIT: Fractions cannot work with BigNumber though, you'll have to calculate with fractions from the start.

@nycos62
Copy link
Author

nycos62 commented Mar 16, 2024

thank you very much !!
another stupid question... sorry... if I start from a string formula, which tools are available to get decimal period ? is there really only by fraction that I could achieve this ?

math.config({
  number: 'Fraction'
})
math.simplify('cos(pi/n)'.replace(/n/g, 3),{},{exactFraction:true}).toString()
'cos(pi / 3)'
math.simplify('1/n'.replace(/n/g, 3),{},{exactFraction:true}).toString()
'1 / 3'

I wish I could from whatever str formula find the decimal period

@Yaffle
Copy link
Contributor

Yaffle commented Mar 16, 2024

@nycos62 ,
if you know limits on the denominator and numerator , you can use method from https://stackoverflow.com/questions/14002113/how-to-simplify-a-decimal-into-the-smallest-possible-fraction/14011299#14011299 to convert decimal to fraction, then use fraction to string as @josdejong says

p.s. perhaps, can you just work with fractions all the time?

@josdejong
Copy link
Owner

You can use math.fraction(value).toString().

You should be aware though that the representation may not be exact, for example you may have a value like 0.3333333333, then the question is is this the exact number, or is it a rounded representation of 1/3 and should it have an infinite number or digits? So, be careful with the precision you need to work with.

@nycos62
Copy link
Author

nycos62 commented Mar 18, 2024

I might have found some sort of middle solution...

math.config({
  number: 'BigNumber',      // Default type of number:
                            // 'number' (default), 'BigNumber', or 'Fraction'
  precision: 509             // Number of significant digits for BigNumbers
}) 

const a = math.bignumber("205.16666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667");
const b = math.bignumber("185.85714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714285714286");
const c = math.bignumber("19.129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258064516129032258065");
const d = math.bignumber("26.708333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333");
const e = math.bignumber("53.071428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571");

function findLongestSubstring (str) {
  let candidate = "";
  for (let i = 1; i <= str.length - i; i++) {
    if (str.indexOf(str.substring(0, i), i) === i)
      candidate = str.substring(0, i);
  }
  return candidate;
}

function rotateAndMoveLeft (str, substr, fromIndex) {
  const rotate = (str) => `${str[str.length-1]}${str.slice(0, str.length-1)}`;

  const lastIndex = substr.length - 1;
  let rotatedStr = substr;
  let pos;
  // console.log(`str=${str}, substr=${substr}, fromIndex=${fromIndex}`);
  for (pos = fromIndex - 1; pos >= 0; pos--) {
    if (rotatedStr[lastIndex] === str[pos]) {
      rotatedStr = rotate(rotatedStr);
    } else {
      pos++;
      break;
    }
  }
  const from = pos !== -1 ? pos : 0;
  return {
    subStr: rotatedStr,
    from,
    numMoved: fromIndex - from
  };
}

function shrinkPattern (pattern) {
  const _shrink = (head, tail) => {
    if (tail.length === 0)
      return head;
    return tail.split(head).every(item => item.length === 0) ?
      head : _shrink(`${head}${tail[0]}`, tail.slice(1));
  }
  return _shrink(pattern[0], pattern.slice(1));
}

function testRepeatingDigits (num) {

  const str = num.toString().substring(0,num.toString().length-3);
  const idx = str.indexOf('.');
  if (idx < 0)
    return false;
  const digitStr = str.substring(idx + 1);
  const [...digits] = digitStr;

  // the first guess of repeating pattern from the right-most digit
  const init = [...findLongestSubstring(digits.slice(0).reverse().join(''))].reverse().join('');

  // no repeating patterns found
  if (init.length === 0)
    return {
      result: (Math.round(num * 100) / 100).toString(),
      pattern: "None"
    };

  // rotate the first guessed pattern to the left to find the beginning of the repeats
  const searchFrom = digitStr.length - (init.length * 2);
  const { subStr, from, numMoved } = searchFrom > 0 ?
    rotateAndMoveLeft(digitStr, init, searchFrom) : { subStr: init, from: 0, numMoved: 0 };

  // shrink the pattern to minimum
  const pattern = shrinkPattern(subStr);

  // truncate the digits overflows the two repeatings of the pattern
  return {
    result: `${str.substring(0, idx+1)}${digitStr.substring(0, from + pattern.length * 2)}...`,
    pattern
  };
}

function toStringVinculum(n) {
  let test = testRepeatingDigits(n);
  let overlineChar = '\u0305';//not compatible on android :/ , so usage of span
  let nStr = n.toString();
  if (test == false || test.pattern == 'None')
    return n.toString();
  else
  {
    return test.result.substring(0,test.result.length-(3+(2*test.pattern.length))) + '<span style="text-decoration:overline">' +test.pattern.split('').join('</span><span style="text-decoration:overline">') + '</span>';
  }  
}

console.log(toStringVinculum(a));
console.log(toStringVinculum(b));
console.log(toStringVinculum(c));
console.log(toStringVinculum(d));
console.log(toStringVinculum(e));

'205.1‾6'
'185.‾857142'
'19.‾129032258064516'
'26.708‾3'
'53.0‾714285'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants