Fraction.js icon indicating copy to clipboard operation
Fraction.js copied to clipboard

Question about `log`s & `pow`s in base 10

Open Rudxain opened this issue 3 years ago • 6 comments

Why when a Number is converted to a Fraction the library internally uses powers and logarithms in base 10? Shouldn't that potentially reduce floating-point precision? Why not base 2?

Rudxain avatar Jun 17 '22 17:06 Rudxain

As long as you're in the MAX_SAFE_INTEGER range, the pow function has no numerical problems. For the log, there might be a problem, but as it is floor'ed, the problem vanishes. When you already use the bigfraction.js, shipped with fraction.js (and eventually becoming the new fraction.js), the problem with MAX_SAFE_INTEGER also vanishes. Do you have examples where this brings in problems?

infusion avatar Jun 17 '22 17:06 infusion

Currently, the only possible problem might be performance, but that's only a concern when dealing with BigInts

Rudxain avatar Jun 17 '22 17:06 Rudxain

Yep, performance is also a concern to me when it comes to switching to BigInt. The code is refactored maybe a million times to come up with a performant implementation of any aspect in the lib. When you see something to improve upon performance, you're more than welcome to bring these things in! :)

infusion avatar Jun 17 '22 17:06 infusion

Speaking of that, I've heard that encapsulation/localization/snapshotting/whatever_name of global objects can increase performance because the engine doesn't need to change the behavior of a library if a piece of code replaces a global property. But I heard in other places that it actually slows down the execution because browsers aren't prepared for that.

What I mean is doing something like:

(function(abs){
  'use strict';
  console.log(abs(-1))
})(Math.abs)

Or maybe:

(function(){
  'use strict';
  const {abs} = Math;
  console.log(abs(-1))
})()

Instead of this:

(function(){
  'use strict';
  console.log(Math.abs(-1))
})()

That's just an example, there's no difference there, but in a library with functions and methods that may be called repeatedly during runtime it makes a difference, because it becomes impossible for external code to modify the internals of the library.

Even MDN recommends it for polyfills. And yes, I'm aware library != polyfill, but the idea can still be worth trying.

I don't recommend doing it until some benchmarks are done on multiple runtime environments, to avoid unnecessary work. I'm in the process of doing that transition for one of my libs, but I did it because I don't like side-effects lol, not just for speed.

It can also reduce the size of the source when minified, although that's not always the case for prototype methods:

(function(){
  'use strict';
  const {map} = Array.prototype;
  console.log( map.call([1, 2, 3], x => x + 1) )
})()

If you don't want to implement it, it's fine. Thank you for your time and reading

Rudxain avatar Jun 17 '22 18:06 Rudxain

As far as I know, in some engines Math.abs is faster since they mark Math.abs as not changed (by a polyfill for example) and link the symbol "Math.abs" directly to improve performance. Thats why those symbols are not shortened in Fraction.js. Besides that repeating symbols over and over again also improves gzip compression ratio to speed up data transfer. The size of the actual code does not matter, since most connections support gzip. That's also why closure compiler optimizes for that. But those are micro optimizations, algorithmic optimizations might improve performance much more

infusion avatar Jun 17 '22 18:06 infusion

Interesting, thank you for the info. And I too agree that algorithmic optimizations are much more important because those are usually the responsibility of devs instead of compilers, and because they are higher-level

Rudxain avatar Jun 17 '22 23:06 Rudxain

I'll close the ticket for now, feel free to bring up other optimizations on Fraction.js :)

infusion avatar Dec 13 '22 10:12 infusion