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

Math.log2 should be exact for exact powers #323

Closed
wants to merge 6 commits into from
Closed

Math.log2 should be exact for exact powers #323

wants to merge 6 commits into from

Conversation

Yaffle
Copy link
Contributor

@Yaffle Yaffle commented Feb 26, 2015

Math.log2(1.0 * (1 << 29)) === 29.000000000000004 in Firefox

Math.log2(1.0 * (1 << 29)) === 29.000000000000004 in Firefox
@Yaffle Yaffle closed this Feb 26, 2015
@cscott
Copy link
Collaborator

cscott commented Feb 26, 2015

Really? Does the spec say this should be exact?

@@ -1594,6 +1604,10 @@
Math.imul = MathShims.imul;
}

if (Math.log2(536870912.0) !== 29) {
Math.log2 = MathShims.log2;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would need to use defineProperty to preserve non-enumerability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ljharb , assignment preservers enumerability (previously I thought it sets all flags to "true", but it preservers it, seems)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh, indeed it does, disregard my comment then :-) (although, if there were an engine where it was enumerable, this would be an opportunity to correct that)

@ljharb
Copy link
Collaborator

ljharb commented Feb 27, 2015

I'm all for making Math shims more accurate, even if the spec doesn't require it.

However, we'd need the following:

  • tests that ensure the behavior is correct
  • example engines that implement this correctly
  • a link to the relevant spec section that, at the least, leaves it to "implementation-dependent"

@Yaffle Yaffle reopened this Apr 7, 2015
@cscott
Copy link
Collaborator

cscott commented Apr 7, 2015

Well, at least two projects that I know of have recently switched to core-js for their shims because es6-shim was "too slow". Adding extra cruft to methods to obtain accuracy not required by the spec will not help es6-shim if it slows down the library for all users.

@Yaffle
Copy link
Contributor Author

Yaffle commented Apr 7, 2015

@ljharb , I have updated my patch a little

if (value === 0) {
return -1 / 0;
}
var e = Math.min(Math.floor(Math.log(value) / Math.LN2), 1023);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of 1023 there should be Math.floor(Math.log(Number.MAX_VALUE) / Math.LN2) - 1 constant or something (?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that's where 1023 comes from, then absolutely a named constant would be ideal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to think how to change this.
Seems, this is a little wrong way of integer part calculation...

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2015

@cscott What is "too slow"? Which methods? Did they use profiling to determine that?

@@ -1498,7 +1498,11 @@
},

log2: function (value) {
return Math.log(value) * Math.LOG2E;
if (value === 0) {
return -1 / 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this just be -Infinity? It's a keyword, so there's no advantage to the division by zero.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you want

@zloirock
Copy link

zloirock commented Apr 7, 2015

@ljharb

What is "too slow"? Which methods? Did they use profiling to determine that?

Simple example, in all modern environment with fully complying with the specification Map and Set, es6-shim replaces it to slow shim with O(n) search complexity for objects. Try with es6-shim and without:

var array = [];
for(var i = 0; i < 100000; i++)array.push([{}, {}]);
array = array.concat(array);
console.time('Map test');
var map = new Map(array);
console.log('Map size: ' + map.size);
console.timeEnd('Map test');

BTW, in the specification clearly states:

Map object must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection.

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2015

@zloirock they're not actually fully complying if the shim is replacing them (zero engines fully comply at the moment). But yes, that is a limitation, since it's impossible to a) not mutate the objects and also b) have O(1) lookup for them.

However, I've filed #326 so that in this situation, we use the native Map for O(1) lookup, which should alleviate the speed issue.

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2015

@Yaffle This code is totally fine now, thanks! Could you also add a test that fails without your changes?

@zloirock
Copy link

zloirock commented Apr 7, 2015

@ljharb

they're not actually fully complying if the shim is replacing them

they're fully complying, but your subclassing detection is broken :)

that is a limitation, since it's impossible

but why core-js and some other shims polyfill it? :D

not mutate the objects

in the spec I don't see anything about it.

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2015

@zloirock core-js must be modifying the objects. I'd be happy to accept a pull request if you know of a way to do it, or a link to their implementation. Nothing must ever mutate unless the spec requires it to, so if it's mutating, it's violating the spec.

As for the subclassing detection, please file an issue or open a PR, but I don't think it's broken - you can't actually subclass the native Map/Set implementations at the moment precisely because of how the test fails without the shim ¯_(ツ)_/¯

@Yaffle
Copy link
Contributor Author

Yaffle commented Apr 7, 2015

@zloirock object mutation is not good and may even affect performance (I hope yo read this page - https://developers.google.com/v8/design )
Anyway, this depends on the use case: if some user wants to store objects in Map AND thinks that it is OK to mutate them, then He can use core-js.

If the number of object is not big or he can map.set(object.uid) or he wants to store only numbers/strings, he may use es6-shim.

@zloirock
Copy link

zloirock commented Apr 7, 2015

@Yaffle broken and slow collections much worse :) Currently, it's required only for old IE, other actual browsers has native collections. Better fix broken methods than replace all collection :)

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2015

Let's debate this on another issue, this isn't the one for it, but no, slow is infinitely better than broken (ours is not broken in any way), and mutation is absolutely broken (and as pointed out, insanely deoptimizes objects in v8, so it'd end up being slower in many ways as well).

@zloirock
Copy link

zloirock commented Apr 7, 2015

@ljharb this line should not work in ES6. All modern browsers, includes V8, contains native collections with correct logic. All from me :)

@zloirock
Copy link

zloirock commented Apr 7, 2015

Oh, it's more: babel/babel#1172

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2015

@zloirock How then can you subclass Map/Set without class? I'll confirm with TC39 but I'm reasonably sure that's, at best, an unintended side effect.

@zloirock
Copy link

zloirock commented Apr 7, 2015

@ljharb see link. It's only emulation, but correct in most case and much better replacing correct collection to broken.

@Yaffle Yaffle closed this Apr 8, 2015
@Yaffle
Copy link
Contributor Author

Yaffle commented Jan 5, 2016

@ljharb , example engines that implement this correctly: the engine of Edge, Chrome, Safari, Firefox (with an exception of Math.log2 results under Windows), Konqueror

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

Successfully merging this pull request may close these issues.

None yet

4 participants