Related
I have a signed value given as a hex number, by example 0xffeb and want convert it into -21 as a "normal" Javascript integer.
I have written some code so far:
function toBinary(a) { //: String
var r = '';
var binCounter = 0;
while (a > 0) {
r = a%2 + r;
a = Math.floor(a/2);
}
return r;
}
function twoscompl(a) { //: int
var l = toBinaryFill(a).length;
var msb = a >>> (l-1);
if (msb == 0) {
return a;
}
a = a-1;
var str = toBinary(a);
var nstr = '';
for (var i = 0; i < str.length; i++) {
nstr += str.charAt(i) == '1' ? '0' : '1';
}
return (-1)*parseInt(nstr);
}
The problem is, that my function returns 1 as MSB for both numbers because only at the MSB of the binary representation "string" is looked. And for this case both numbers are 1:
-21 => 0xffeb => 1111 1111 1110 1011
21 => 0x15 => 1 0101
Have you any idea to implement this more efficient and nicer?
Greetings,
mythbu
Use parseInt() to convert (which just accepts your hex string):
parseInt(a);
Then use a mask to figure out if the MSB is set:
a & 0x8000
If that returns a nonzero value, you know it is negative.
To wrap it all up:
a = "0xffeb";
a = parseInt(a, 16);
if ((a & 0x8000) > 0) {
a = a - 0x10000;
}
Note that this only works for 16-bit integers (short in C). If you have a 32-bit integer, you'll need a different mask and subtraction.
I came up with this
function hexToInt(hex) {
if (hex.length % 2 != 0) {
hex = "0" + hex;
}
var num = parseInt(hex, 16);
var maxVal = Math.pow(2, hex.length / 2 * 8);
if (num > maxVal / 2 - 1) {
num = num - maxVal
}
return num;
}
And usage:
var res = hexToInt("FF"); // -1
res = hexToInt("A"); // same as "0A", 10
res = hexToInt("FFF"); // same as "0FFF", 4095
res = hexToInt("FFFF"); // -1
So basically the hex conversion range depends on hex's length, ant this is what I was looking for. Hope it helps.
Based on #Bart Friederichs I've come with:
function HexToSignedInt(num, numSize) {
var val = {
mask: 0x8 * Math.pow(16, numSize-1), // 0x8000 if numSize = 4
sub: -0x1 * Math.pow(16, numSize) //-0x10000 if numSize = 4
}
if((parseInt(num, 16) & val.mask) > 0) { //negative
return (val.sub + parseInt(num, 16))
}else { //positive
return (parseInt(num,16))
}
}
so now you can specify the exact length (in nibbles).
var numberToConvert = "CB8";
HexToSignedInt(numberToConvert, 3);
//expected output: -840
function hexToSignedInt(hex) {
if (hex.length % 2 != 0) {
hex = "0" + hex;
}
var num = parseInt(hex, 16);
var maxVal = Math.pow(2, hex.length / 2 * 8);
if (num > maxVal / 2 - 1) {
num = num - maxVal
}
return num;
}
function hexToUnsignedInt(hex){
return parseInt(hex,16);
}
the first for signed integer and
the second for unsigned integer
As I had to turn absolute numeric values to int32 values that range from -2^24 to 2^24-1,
I came up with this solution, you just have to change your input into a number through parseInt(hex, 16), in your case, nBytes is 2.
function toSignedInt(value, nBytes) { // 0 <= value < 2^nbytes*4, nBytes >= 1,
var hexMask = '0x80' + '00'.repeat(nBytes - 1);
var intMask = parseInt(hexMask, 16);
if (value >= intMask) {
value = value - intMask * 2;
}
return value;
}
var vals = [ // expected output
'0x00', // 0
'0xFF', // 255
'0xFFFFFF', // 2^24 - 1 = 16777215
'0x7FFFFFFF', // 2^31 -1 = 2147483647
'0x80000000', // -2^31 = -2147483648
'0x80000001', // -2^31 + 1 = -2147483647
'0xFFFFFFFF', // -1
];
for (var hex of vals) {
var num = parseInt(hex, 16);
var result = toSignedInt(num, 4);
console.log(hex, num, result);
}
var sampleInput = '0xffeb';
var sampleResult = toSignedInt(parseInt(sampleInput, 16), 2);
console.log(sampleInput, sampleResult); // "0xffeb", -21
Based on the accepted answer, expand to longer number types:
function parseSignedShort(str) {
const i = parseInt(str, 16);
return i >= 0x8000 ? i - 0x10000 : i;
}
parseSignedShort("0xffeb"); // -21
function parseSignedInt(str) {
const i = parseInt(str, 16);
return i >= 0x80000000 ? i - 0x100000000 : i;
}
parseSignedInt("0xffffffeb"); // -21
// Depends on new JS feature. Only supported after ES2020
function parseSignedLong(str) {
if (!str.toLowerCase().startsWith("0x"))
str = "0x" + str;
const i = BigInt(str);
return Number(i >= 0x8000000000000000n ? i - 0x10000000000000000n : i);
}
parseSignedLong("0xffffffffffffffeb"); // -21
I'm trying to transform an array of numbers such that each number has only one nonzero digit.
so basically
"7970521.5544"
will give me
["7000000", "900000", "70000", "500", "20", "1", ".5", ".05", ".004", ".0004"]
I tried:
var j = "7970521.5544"
var k =j.replace('.','')
var result = k.split('')
for (var i = 0; i < result.length; i++) {
console.log(parseFloat(Math.round(result[i] * 10000) /10).toFixed(10))
}
Any ideas, I'm not sure where to go from here?
Algorithm:
Split the number in two parts using the decimal notation.
Run a for loop to multiply each digit with the corresponding power of 10, like:
value = value * Math.pow(10, index); // for digits before decimal
value = value * Math.pow(10, -1 * index); // for digits after decimal
Then, filter the non-zero elements and concatenate both the arrays. (remember to re-reverse the left-side array)
var n = "7970521.5544"
var arr = n.split('.'); // '7970521' and '5544'
var left = arr[0].split('').reverse(); // '1250797'
var right = arr[1].split(''); // '5544'
for(let i = 0; i < left.length; i++)
left[i] = (+left[i] * Math.pow(10, i) || '').toString();
for(let i = 0; i < right.length; i++)
right[i] = '.' + +right[i] * Math.pow(10, -i);
let res = left.reverse() // reverses the array
.filter(n => !!n)
// ^^^^^^ filters those value which are non zero
.concat(right.filter(n => n !== '.0'));
// ^^^^^^ concatenation
console.log(res);
You can use padStart and padEnd combined with reduce() to build the array. The amount you want to pad will be the index of the decimal minus the index in the loop for items left of the decimal and the opposite on the right.
Using reduce() you can make a new array with the padded strings taking care to avoid the zeroes and the decimal itself.
let s = "7970521.5544"
let arr = s.split('')
let d_index = s.indexOf('.')
if (d_index == -1) d_index = s.length // edge case for nums with no decimal
let nums = arr.reduce((arr, n, i) => {
if (n == 0 || i == d_index) return arr
arr.push((i < d_index)
? n.padEnd(d_index - i, '0')
: '.' + n.padStart(i - d_index, '0'))
return arr
}, [])
console.log(nums)
You could split your string and then utilize Array.prototype.reduce method. Take note of the decimal position and then just pad your value with "0" accordingly. Something like below:
var s = "7970521.5544";
var original = s.split('');
var decimalPosition = original.indexOf('.');
var placeValues = original.reduce((accum, el, idx) => {
var f = el;
if (idx < decimalPosition) {
for (let i = idx; i < (decimalPosition - 1); i++) {
f += "0";
}
accum.push(f);
} else if (idx > decimalPosition) {
let offset = Math.abs(decimalPosition - idx) - 2;
for (let i = 0; i <= offset; i++) {
f = "0" + f;
}
f = "." + f;
accum.push(f);
}
return accum;
}, []);
console.log(placeValues);
Shorter alternative (doesn't work in IE) :
var s = "7970521.5544"
var i = s.split('.')[0].length
var a = [...s].reduce((a, c) => (i && +c && a.push(i > 0 ?
c.padEnd(i, 0) : '.'.padEnd(-i, 0) + c), --i, a), [])
console.log( a )
IE version :
var s = "7970521.5544"
var i = s.split('.')[0].length
var a = [].reduce.call(s, function(a, c) { return (i && +c && a.push(i > 0 ?
c + Array(i).join(0) : '.' + Array(-i).join(0) + c), --i, a); }, [])
console.log( a )
function standardToExpanded(n) {
return String(String(Number(n))
.split(".")
.map(function(n, i) {
// digits, decimals..
var v = n.split("");
// reverse decimals..
v = i ? v.reverse() : v;
v = v
.map(function(x, j) {
// expanded term..
return Number([x, n.slice(j + 1).replace(/\d/g, 0)].join(""));
})
.filter(Boolean); // omit zero terms
// unreverse decimals..
v = i ? v.map(function(x) {
return '.' + String(x).split('').reverse().join('')
}).reverse() : v;
return v;
})).split(',');
}
console.log(standardToExpanded("7970521.5544"));
// -> ["7000000", "900000", "70000", "500", "20", "1", ".5", ".05", ".004", ".0004"]
This looks like something out of my son's old 3rd Grade (core curriculum) Math book!
I use rewrited hsCountdown library and for some numbers it show wrong values:
In example above I specified "130" for increment to, but hsCountdown incrementing it only to 125. Why?
I was debug for "r" variable (on line where located #debug_console) and what you can mean? This variable magically was increment not by integer number, but float.
For example, 54.0000000001 instead of 54. JavaScript, you're so drunk!
(function(a) {
"use strict";
a.fn.hsCounter = function(b) {
var c = a.extend({
delay: 50,
signPos: "after",
classVisible: "countup-vis",
decimalSeparator: ".",
orderSeparator: " "
}, b);
return this.each(function() {
b && a.extend(c, b);
var timer, num, line, content, self = a(this),
win = a(window),
winTop = win.scrollTop(),
winHeight = win.height(),
numb = self.data("num"),
increment = self.data("increment") ? self.data("increment") : (numb / 25 > 1.0 ? numb / 25 : 1.0),
fractional = self.data("fractional") ? self.data("fractional") : 0,
sign = self.data("sign") ? self.data("sign") : "",
regExp = /(\d)(?=(\d\d\d)+([^\d]|$))/g,
start = self.data("start") ? +self.data("start") : 0,
amount = a(".countup-amount"),
realMaxNumber = numb - increment;
winTop <= self.offset().top && winTop + winHeight >= self.offset().top && (timer = setTimeout(function u() {
var currentIncrement = (fractional > 0 ? start.toFixed(fractional) : start);
$('#debug_console').append('[Condition debug] (currentIncrement <= realMaxNumber) equals ('+currentIncrement+' <= '+realMaxNumber+')<br>');
return (currentIncrement <= realMaxNumber)
?
(start += increment, num = start.toFixed(fractional).replace(".", c.decimalSeparator).replace(regExp, "$1" + c.orderSeparator), content = self.find(amount).html(num), "after" == c.signPos ? self.html('<span class="countup-amount">' + num + '</span><span class="countup-sign">' + sign + "</span>") : "before" == c.signPos && self.html('<span class="countup-sign">' + sign + '</span><span class="countup-amount">' + num + "</span>"), self.hasClass("progress-up") && (self.html(self.html() + "<ins/>"), self.find("ins").css("width", start + "%")), self.parent().hasClass("countup-wrap") && (line = self.parent().find(".countup-line"), line.css("width", start + "%")), void(timer = setTimeout(u, c.delay)))
:
(start = numb, !0);
}, c.delay));
});
}
})(jQuery);
function initCounter(a, b) { b ? a.hsCounter() : a.text("0"); }
initCounter($(".counterup"), 1);
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<h1 class="counterup js" data-num="130">...</h1>
<div id="debug_console"></div>
The issue relies in these lines:
n = h.data("increment") ? h.data("increment") : (l / 25 > 1.0 ? l / 25 : 1.0),
and t = l - n;
t is basically the number that this code will count up to. t is calculated as l (which is the data-num attribute, 130 in this case minus n which is the derived from the data-increment attribute.
As you can see, you did not provide a data-increment, and the code defaults to l / 25, which is 130 / 25 = 5.2. Then, t is will actually be l - n = 130 - 5.2 = 124.8, and this is why it counts to 125 instead of 30.
A simple solution will be to add data-increment="1" to your h1 tag. You can consider changing the default calculation for the n variable too.
EDIT:
I see that you edited your code, which is great because it's much easier to debug. I think the solution whould be to change this line: (start = numb, !0); to something like this: self.find(".countup-amount").html(numb); It basically means the if the current number + increment value is greater than the target value, it means it is the last step, and we can simply populate our span with the target value.
Fixed JS code
(function(a) {
"use strict";
a.fn.hsCounter = function(b) {
var c = a.extend({
delay: 50,
signPos: "after",
classVisible: "countup-vis",
decimalSeparator: ".",
orderSeparator: " "
}, b);
return this.each(function() {
b && a.extend(c, b);
var timer, num, line, content, self = a(this),
win = a(window),
winTop = win.scrollTop(),
winHeight = win.height(),
numb = self.data("num"),
fractional = self.data("fractional") ? self.data("fractional") : 0,
decim = fractional > 0 ? 0.5 : 1.0,
increment = self.data("increment") ? self.data("increment") : (numb / 25 > decim ? numb / 25 : decim),
sign = self.data("sign") ? self.data("sign") : "",
regExp = /(\d)(?=(\d\d\d)+([^\d]|$))/g,
start = self.data("start") ? +self.data("start") : 0,
amount = a(".countup-amount");
winTop <= self.offset().top && winTop + winHeight >= self.offset().top && (timer = setTimeout(function u() {
if ((fractional > 0 ? start.toFixed(fractional) : start) < numb) {
var stin = (start + increment > numb) ? (start = numb) : (start += increment);
return (stin, num = start.toFixed(fractional).replace(".", c.decimalSeparator).replace(regExp, "$1" + c.orderSeparator), content = self.find(amount).html(num), "after" == c.signPos ? self.html('<span class="countup-amount">' + num + '</span><span class="countup-sign">' + sign + "</span>") : "before" == c.signPos && self.html('<span class="countup-sign">' + sign + '</span><span class="countup-amount">' + num + "</span>"), self.hasClass("progress-up") && (self.html(self.html() + "<ins/>"), self.find("ins").css("width", start + "%")), self.parent().hasClass("countup-wrap") && (line = self.parent().find(".countup-line"), line.css("width", start + "%")), void(timer = setTimeout(u, c.delay)));
} else {
return (start = numb, !0);
}
}, c.delay));
});
}
})(jQuery);
function initCounter(a, b) { b ? a.hsCounter() : a.text("0"); }
initCounter($(".counterup"), 1);
Doing a survey where a user picks :
A B or C
I then need to know if the user has picked Mostly A's, B's or C's.
I'm trying a few jQuery logics' but not having much luck, Due to expression expected error.
Is there a neater / better way to show purely which variable is the highest?
My current jQuery :
var count = 0;
var count_a = 0;
var count_b = 0;
var count_c = 0;
$('.radio-select').click(function()
{
var chosen_option = $(this).val();
if(chosen_option == 'a')
{
count++;
count_a ++;
}
if(chosen_option == 'b')
{
count++;
count_b ++;
}
if(chosen_option == 'c')
{
count++;
count_c ++;
}
check_numbers(count, count_a, count_b, count_c);
})
function check_numbers(count, a, b, c)
{
parseInt(a);
parseInt(b);
parseInt(c);
if(count == '8')
{
if ((a > b ) && (a > c))
{
alert("A is Highest");
}
if ((b > a ) && (b > c))
{
alert("B is Highest");
}
if(c > b) && (c > a))
{
alert("C is highest!");
}
}
}
jsFiddle Example
If you wanted a smaller way of doing it you could use inline if statements. Up to you if this is a better way, I like it though.
a = 5
b = 11
c = 6
console.log((a > b && a > c? a : (b > c ? b : c)))
Firstly you do not need to use parseInt() on a, b, c as they are already integers. And again count is an integer while you are comparing it to a string. This should work.
if(count == 8)
{
if ((a > b ) && (a > c))
{
alert("A is Highest");
}
else if ((b > a ) && (b > c))
{
alert("B is Highest");
}
else
{
alert("C is highest!");
}
You need to fetch the value returned by parseInt. Use it like: a = parseInt(a); and same for the other variables before comparing them in the if...else.
function check_numbers(count, a, b, c)
{
var x = parseInt(a),
y = parseInt(b),
z = parseInt(c);
if(count == 8)
{
var result = (x > y ? (x > z ? x : z) : (y > z ? y : z));
}
}
#StuBlackett you can consider adding the values and labels to an array then sorting Descending and returning the lable at the top.
function CompareIndexZero(a, b) {
if (a[0] < b[0]) return 1;
if (a[0] > b[0]) return -1;
return 0;
}
function myFunction() {
var count_a = 2;
var count_b = 5;
var count_c = 4;
var arrHighest = [];
arrHighest.push([count_a, "A"]);
arrHighest.push([count_b, "B"]);
arrHighest.push([count_c, "C"]);
arrHighest.sort(CompareIndexZero);
alert(arrHighest[0][1] + " is the highest");
}
Here is a modified version of check_numbers() that works as intended if I got you right. The point I want to make is the use of Math.max() to find the highest number from a selection of numbers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
function check_numbers(count, a, b, c) {
if(count === 8) {
var numArray = [a, b, c];
var highest = Math.max.apply(null, numArray);
console.log(highest);
if (highest === a) {
console.log('a is highest');
} else if (highest === b) {
console.log('b is highest');
} else if (highest === c) {
console.log('c is highest');
}
}
}
check_numbers(8, 1 , 2, 5);
check_numbers(8, 5, 2, 1);
check_numbers(8, 1 , 5, 2);
Have you also taken into account that multiple answers could share the highest count?
My 2 cents on that:
var count = count_a = count_b = count_c = 0;
$('.radio-select').on('click', function() {
var chosen_option = $(this).val();
if (chosen_option == 'a') {
count_a++;
}
else if (chosen_option == 'b') {
count_b++;
}
else if (chosen_option == 'c') {
count_c++;
}
if (++count == 8) {
check_numbers(count_a, count_b, count_c);
}
});
function check_numbers(a, b, c) {
var highest = ((a > b && a > c) ? a : (b > c)? b : c),
multiple = false,
alertText = '';
if (a == highest) {
alertText += 'A';
}
if (b == highest) {
if (alertText != '') {
multiple = true;
alertText += ' and ';
}
alertText += 'B';
}
if (c == highest) {
if (alertText != '') {
multiple = true;
alertText += ' and ';
}
alertText += 'C';
}
alert(alertText + ' ' + (multiple ? 'are' : 'is') + ' highest!');
}
function num(n) {
if (n >= 1000 && n < 10000) return (n/1000).toFixed(3) + "K";
if (n >= 10000 && n < 100000) return (n/1000).toFixed(1) + "K";
if (n >= 100000 && n < 1000000) return (n/1000).toFixed(0) + "K";
if (n >= 1000000 && n < 10000000) return (n/1000000).toFixed(3) + "M";
if (n >= 10000000 && n < 100000000) return (n/1000000).toFixed(1) + "M";
if (n >= 100000000 && n < 1000000000) return (n/1000000).toFixed(0) + "M";
if (n >= 1000000000 && n < 10000000000) return (n/1000000000).toFixed(3) + "B";
if (n >= 10000000000 && n < 100000000000) return (n/1000000000).toFixed(1) + "B";
if (n >= 100000000000 && n < 1000000000000) return (n/1000000000).toFixed(0) + "B";
if (n >= 1000000000000 && n < 10000000000000) return (n/1000000000000).toFixed(3) + "T";
if (n >= 10000000000000 && n < 100000000000000) return (n/1000000000000).toFixed(1) + "T";
if (n >= 100000000000000 && n < 1000000000000000) return (n/1000000000000).toFixed(0) + "T";
return n;
}
Since at some point I'm probably going to be going upwards to the power of hundreds, is there an easier way to do this?
function formatNumber(number) {
var i = 0; units = [ "", "K", "M", "B", "T" ]; // etc
while (number > 1000) {
number /= 1000;
i += 1;
}
return Math.floor(number * 1000) / 1000 + units[i];
}
formatNumber(1234567); // 1.234M
formatNumber(1230567); // 1.23M
This might be faster for very large numbers:
function formatNumber(number) {
var i; units = [ "", "K", "M", "B", "T" ]; // etc
i = Math.round(Math.log(number) / Math.log(10) / 3);
number /= Math.pow(10, i * 3);
return Math.floor(number * 1000) / 1000 + units[i];
}
formatNumber(1234567); // 1.234M
formatNumber(1230567); // 1.23M
My approach here:
function num(n) {
if(n < 1000) {
return n
}
var units = ['K', 'M', 'B', 'T']
for(var i = -1; n >= 1000; i++) {
n /= 1000
}
if(n >= 100) {
return n.toFixed(0) + units[i]
}
if(n >= 10) {
return n.toFixed(1) + units[i]
}
return n.toFixed(3) + units[i]
}
Here's the finished function with help from Halcyon.
function fnum(number) {
var i, x, units = [ "", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc" ];
i = Math.floor(Math.log(number) / Math.log(10) / 3);
x = Math.floor(Math.log(number) / Math.log(10)) % 3;
number /= Math.pow(10, i * 3);
return ((number * 1000) / 1000).toFixed(77 % ((x * 2) + 3)) + units[i];
}
Will return a number from 0 to 999 to 2 decimal places when formatted to 1s and 10s, and 0 decimal places when formatted to 100s, and then add a numerical suffix. Add additional suffixes as required.
fnum(22000); >> 22.00K
fnum(153000000); >> 153M
fnum(2,230,000,000,000); >> 2.23T
function fnum(number) {
var i, x, units = [ "", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc" ];
i = Math.floor(Math.log(number) / Math.log(10) / 3);
x = Math.floor(Math.log(number) / Math.log(10)) % 3;
number /= Math.pow(10, i * 3);
return ((number * 1000) / 1000).toFixed(77 % ((x * 2) + 3)) + units[i];
}
console.log(fnum(22000))
console.log(fnum(153000000))
console.log(fnum(2200000000000))