C's math.frexp in JavaScript

Here’s what our current JavaScript equivalent to C's frexp found in the math.h header file looks like.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
module.exports = function frexp (arg) {
// discuss at: http://locutus.io/c/frexp/
// original by: Oskar Larsson Högfeldt (http://oskar-lh.name/)
// note 1: Instead of
// note 1: double frexp( double arg, int* exp );
// note 1: this is built as
// note 1: [double, int] frexp( double arg );
// note 1: due to the lack of pointers in JavaScript.
// note 1: See code comments for further information.
// example 1: frexp(1)
// returns 1: [0.5, 1]
// example 2: frexp(1.5)
// returns 2: [0.75, 1]
// example 3: frexp(3 * Math.pow(2, 500))
// returns 3: [0.75, 502]
// example 4: frexp(-4)
// returns 4: [-0.5, 3]
// example 5: frexp(Number.MAX_VALUE)
// returns 5: [0.9999999999999999, 1024]
// example 6: frexp(Number.MIN_VALUE)
// returns 6: [0.5, -1073]
// example 7: frexp(-Infinity)
// returns 7: [-Infinity, 0]
// example 8: frexp(-0)
// returns 8: [-0, 0]
// example 9: frexp(NaN)
// returns 9: [NaN, 0]
// Potential issue with this implementation:
// the precisions of Math.pow and the ** operator are undefined in the ECMAScript standard,
// however, sane implementations should give the same results for Math.pow(2, <integer>) operations
// Like frexp of C and std::frexp of C++,
// but returns an array instead of using a pointer argument for passing the exponent result.
// Object.is(n, frexp(n)[0] * 2 ** frexp(n)[1]) for all number values of n except when Math.isFinite(n) && Math.abs(n) > 2**1023
// Object.is(n, (2 * frexp(n)[0]) * 2 ** (frexp(n)[1] - 1)) for all number values of n
// Object.is(n, frexp(n)[0]) for these values of n: 0, -0, NaN, Infinity, -Infinity
// Math.abs(frexp(n)[0]) is >= 0.5 and < 1.0 for any other number-type value of n
// See http://en.cppreference.com/w/c/numeric/math/frexp for a more detailed description
arg = Number(arg)
const result = [arg, 0]
if (arg !== 0 && Number.isFinite(arg)) {
const absArg = Math.abs(arg)
// Math.log2 was introduced in ES2015, use it when available
const log2 = Math.log2 || function log2 (n) { return Math.log(n) * Math.LOG2E }
let exp = Math.max(-1023, Math.floor(log2(absArg)) + 1)
let x = absArg * Math.pow(2, -exp)
// These while loops compensate for rounding errors that sometimes occur because of ECMAScript's Math.log2's undefined precision
// and also works around the issue of Math.pow(2, -exp) === Infinity when exp <= -1024
while (x < 0.5) {
x *= 2
exp--
}
while (x >= 1) {
x *= 0.5
exp++
}
if (arg < 0) {
x = -x
}
result[0] = x
result[1] = exp
}
return result
}
[ View on GitHub | Edit on GitHub | Source on GitHub ]

How to use

You you can install via npm install locutus and require it via require('locutus/c/math/frexp'). You could also require the math module in full so that you could access math.frexp instead.

If you intend to target the browser, you can then use a module bundler such as Browserify, webpack or rollup.js.

ES5/ES6

This function targets ES5, but as of Locutus 2.0.2 we also support ES6 functions. Locutus transpiles to ES5 before publishing to npm.

A community effort

Not unlike Wikipedia, Locutus is an ongoing community effort. Our philosophy follows The McDonald’s Theory. This means that we don't consider it to be a bad thing that many of our functions are first iterations, which may still have their fair share of issues. We hope that these flaws will inspire others to come up with better ideas.

This way of working also means that we don't offer any production guarantees, and recommend to use Locutus inspiration and learning purposes only.

Notes

  • Instead of double frexp( double arg, int* exp ); this is built as [double, int] frexp( double arg ); due to the lack of pointers in JavaScript. See code comments for further information.

Examples

Please note that these examples are distilled from test cases that automatically verify our functions still work correctly. This could explain some quirky ones.

#codeexpected result
1frexp(1)[0.5, 1]
2frexp(1.5)[0.75, 1]
3frexp(3 * Math.pow(2, 500))[0.75, 502]
4frexp(-4)[-0.5, 3]
5frexp(Number.MAX_VALUE)[0.9999999999999999, 1024]
6frexp(Number.MIN_VALUE)[0.5, -1073]
7frexp(-Infinity)[-Infinity, 0]
8frexp(-0)[-0, 0]
9frexp(NaN)[NaN, 0]

Ehm.. Only 2 C functions in all of Locutus?

We could still assimilate many more functions to this language. We only just rolled out multilingual support to Locutus. If you fancy a challenge, we'd love your help expanding that. For instance, you could:

We will then review it. If it's useful to the project and in line with our contributing guidelines your work will become part of Locutus and you'll be automatically credited in the authors section accordingly.


« More C math functions