5
\$\begingroup\$

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?

\$\endgroup\$
  • \$\begingroup\$ I upvoted earlier, but I'm afraid I now had to vote to close. After looking at this more closely, this appears to be hypothetical code, which is off-topic for CodeReview. Besides a couple of actual errors in the code (extra single quote, missing close-brace), it currently doesn't actually do a full conversion. So from a review standpoint there's little to do, because it's not finished. But if you have more complete code that does the full conversion, feel free to post it. \$\endgroup\$ – Flambino May 15 '15 at 22:51
  • \$\begingroup\$ updated with working code \$\endgroup\$ – boatSoap May 15 '15 at 23:49
  • \$\begingroup\$ And I've retracted my close vote. However, I'm not sure about your conversion. As far as I can tell, from reading a little, 8-bit floats express integer values (range -122880 to 122880 with poor precision), not fractional values. So I get a value of 122 from 0x26. \$\endgroup\$ – Flambino May 16 '15 at 0:32
  • \$\begingroup\$ How to split the binary string is an issue. The protocol I am converting (GRIB) does not state how it is split but I know that in this example there must be a decimal because wind speeds of 122 m/s or 272 mph isn't feasible at sea-level. I was basing my conversion off this post \$\endgroup\$ – boatSoap May 19 '15 at 16:42
  • \$\begingroup\$ Having looked at it again, I'm back to my original conclusion: It's broken. Not because of the string parsing itself (although that's most likely broken as well), but because everything before the parsing is broken: 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\$ – Flambino May 19 '15 at 17:57
2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.