To start, I am in the wrong language and I think it is time learn some C++ and compile it as an add-on for NodeJS. For now, though, I have a few code snippets that work that may be interesting or may be an abomination of coding.
This code works with weather forecast data, specifically the GRIB-2 file format. GRIB-2 compresses weather information into either integers or floating point values depending on the type (this cast is wind gusts). The data has been packed into 8-bit floating point values. I am assuming the GRIB-2 protocol follows floating point arithmetic and we can break our 8-bits into the following sections: 0 000 0000
Code to convert hex: 26 to floating-point: 0.6875:
var bin = makeWhole('100110'); //hex: 26
var fp = nBitFloatPoint(8, bin); //8 is unused but will allow different bit lengths
console.log(fp) // 0.6875
function makeWhole(a){
var hex = a;
if(hex.length !== 8){
hex = prepend(hex);
}
return hex;
}
function prepend(hex){
var diff = 8 - hex.length;
var str = '';
for(var i=0; i<diff; i++){
str += '0';
}
return str + hex;
}
function nBitFloatPoint(n, bin){
var s = bin.charAt(0);
var e = parseInt(bin.substring(1, 4), 2) - 3;
var m = '1.' + bin.substring(4, 8);
var d = shiftDot(m, e);
var result = convert(s, e, d);
return result;
}
function convert(s, e, d){
var strD = d.toString()
var length, dotIndex, num, frac;
var values = [];
length = strD.length;
dotIndex = strD.indexOf('.');
num = strD.substring(0, dotIndex).split('');
frac = strD.substring(dotIndex + 1).split('');
//console.log(num)
for(var i=0; i<num.length; i++){
if(parseInt(num[i])){
values.push(Math.pow(2, i));
}
}
for(var i=0; i<frac.length; i++){
console.log(frac)
if(parseInt(frac[i])){
//console.log(Math.pow(2, -i))
values.push(Math.pow(2, -i-1));
}
}
if(values.length){
return values.reduce(arraySum);
}
return 0;
}
function arraySum(prev, curr, index, arr){
return prev + curr;
}
function shiftDot(m, e){
var str = '00000000000000000000000000' + m;
var dot = str.indexOf('.');
str = str.replace('.', '');
return parseFloat(str.substring(0, dot + e) + '.' + str.substring(dot + e));
}
Is this a really hack idea that is best left to a language mean to work with binary buffers? Is the function shiftDot
a plausible workaround or am I missing something best left to an internal JS function?
1 Answer 1
To start I know nothing about GRIB-2 that being said I know some ways that you could improve your code.
"Magic Numbers"
Generally you shouldn't put numbers in code where their meaning is unknown. You should set the hanging numbers to constants. Javascript (ES5) doesn't have constants but there are ways to fake it. The number "8" in your code is abstract and one would only know what it is for because you told them. But a without that it would be hard for anyone to know what it means from an initial read through of your code.
Possible Solution - You could create a constant object literal.
var Constants = {
FLOAT_BIT_LENGTH: 8
}
console.log(Constants.FLOAT_BIT_LENGTH);
Polluting the Global Namespace
As it goes right now your code is putting a bunch of functions and variables into the Global namespace. You should encapsulate your code in a self invoking function, so you can set variables that are global to your code without foo-ing with the global variable scope, if you want other people to be able to embed your code as a module in someone else’s code.
Possible Solution - Self invoking anonymous function to create scope
(function (document) {
/* code goes here */
})(document);
Non-Descriptive Variables and Parameters
You should be able to read the method signature and know what it does. convert(s, e, d) means nothing to anyone even knowing what the code is for. You could add documentation blocks via JSDocs, and make your function variables more descriptive.
Possible Solution - JSDocs used along with descriptive function arguments
/**
* Converts a foo into a foobar using bar as the radix and baz as the offset
* @param {integer} foo - Does x
* @param {integer} bar - Does y
* @param {float} baz - Does z
* @return {float} - x AND y AND z
*/
convertByRadix(foo, bar, baz)
Not Making Use of Function Expressions in Callbacks
Function expressions can be used as a callback function, they are inline and make your code easier to read when the function is only a line or two and have no side effects. Also you don't need to declare all the parameters of a callback, only the ones you are using.
return values.reduce(function(prev, curr) {
return prev + curr;
});
It may also be worth looking into typed values (that are not boxed) that are available in some Javascript compilers/libraries, like "ASM.js" or "LLJS". As well as looking into possibly using some of Javascripts bitwise operators. Also look into running your code through a linter such as JSHint.
122
from0x26
. \$\endgroup\$GRIB
) does not state how it is split but I know that in this example there must be a decimal because wind speeds of122 m/s
or272 mph
isn't feasible at sea-level. I was basing my conversion off this post \$\endgroup\$makeWhole(0x26)
returns 38. The number 38. Not a string, and certainly not a binary string. So everything from that point on will only make it things worse. It's quite surprising it actually returns anything resembling a float at the end. \$\endgroup\$