-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Number Base conversions #95
Comments
what about using 0c for octal? |
Yes, well actually I don't want to do just conversions but I would like to implement special types for different number bases, like These types should not store their value as as a regular (floating point) number, but in a BigNumber kind of way, with arbitrary precision. That way we can work with values which do not fit in a regular Number and we prevent weird round-off errors. To work with negative numbers, we will have to add an option to specify the number of bytes used to store the number so we can apply the Two's complement rule. |
This is something I'd like to see. Any idea on a timeframe? |
I don't think this will be implemented on short term (like months). We could implement this straight away, but I expect this to be much less work if we first implement a solution to create functions in a modular, extensible way. So instead of one huge function See roadmap for my take on priorities https://github.com/josdejong/mathjs/blob/master/ROADMAP.md, which can be discussed of course. |
Thanks Jos, it might be good to review the roadmap, as some of these items are already done and some still left on ver. 1 branch. |
Hmm, that sounds like an interesting plan. I like the idea of a function composer. |
@husayt I think the roadmap is up to date (most is planned for version 1.x), which ones are already done? |
I wrote an extension to support base-n numbers for my app. You can use it for math.j's; I'm donating it to the public domain. It seems to work, but there are still a few issues:
Example of expected usage: |
/*global math */
/*
issues
- Precision not imported from math instance. ex: 3^(\xa i), (\xF+\xFi)^2. Use factory function? How?
- Conversion object from "BaseNumber" to "number" causes infinite loop. Why? Still works without it. Why?
- Must enclose dec() function to use with function-plot.js
preprocessing code
// from MathProcessor.js
expr = expr.replace(/\\(x[0-9A-Fa-f]*[.]?[0-9A-Fa-f]*)/g, 'baseN("$1")'); // hexadecimal
expr = expr.replace(/\\(o[0-7]*[.]?[0-7]*)/g, 'baseN("$1")'); // octal
expr = expr.replace(/\\(b[0-1]*[.]?[0-1]*)/g, 'baseN("$1")'); // binary
return math.eval(expr, scope);
@license
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
(function () {
"use strict";
// Object Type for Base-N numbers (like hex, oct, bin, etc)
function BaseNumber(vv, bb) {
if (typeof vv === "string" || vv instanceof String) {
return this.parseString(vv, bb);
}
if (vv.isBaseNumber) {
this.value = vv.value;
this.base = bb || vv.base;
} else {
this.value = vv;
this.base = bb;
}
}
BaseNumber.prototype = {
BINID: "\\b",
OCTID: "\\o",
HEXID: "\\x",
base: 10,
value: 0,
isBaseNumber: true,
create: function (vv, bb) {
return new BaseNumber(vv, bb);
},
valueOf: function () {
return this.value;
},
toString: function (bb, vv) {
var txt = "", prefix = "", base = bb || this.base, value = vv || this.value;
if (value instanceof math.type.Complex) {
if (value.re) {
txt = this.toString(base, value.re);
}
if (value.re && value.im) {
if (value.im < 0) {
txt = txt + " - " + this.toString(base, math.abs(value.im)) + "(i)";
} else {
txt = txt + " + " + this.toString(base, value.im) + "(i)";
}
} else if (value.im) {
txt = this.toString(base, value.im) + "(i)";
}
} else {
switch (base) {
case 2:
prefix = this.BINID;
break;
case 8:
prefix = this.OCTID;
break;
case 16:
prefix = this.HEXID;
break;
}
txt = Number((value).valueOf()).toString(base);
if (txt.charAt(0) === "-") {
txt = "-" + prefix + txt.substr(1);
} else {
txt = prefix + txt;
}
}
return txt;
},
parseString: function (vv, bb) {
var txt = String(vv).trim(),
sign = 1,
base = 10,
value = 0,
pre = "";
// Determine sign of x, remove it if so.
if (txt.charAt(0) === "-") {
txt = txt.substr(1);
sign = -1;
}
// Remove a "\" character if it is in front of the string.
if (txt.charAt(0) === "\\") {
txt = txt.substr(1);
}
// Determine if base 2,8,10,16 based on the character in front
// Remove identifying character if so.
if (txt.length > 1) {
pre = txt.charAt(0);
if (pre === "b") {
base = 2;
txt = txt.substr(1);
} else if (pre === "o") {
base = 8;
txt = txt.substr(1);
} else if (pre === "x") {
base = 16;
txt = txt.substr(1);
}
}
/*
Convert remaining text to decimal.
If it has a fractional part, convert that too.
d(i) = digit in place i of string. p = length of string (with no ".").
before the decimal place:
whole part = d(1)*b^(p-1) + d(2)*b^(p-2) + ... d(p-1)*b^1 + d(p)*b^0
whole part = parseInt
after the decimal place:
fractional part = d(1)/b^1 + d(2)/b^2 + ... d(p-1)/b^(p-1) + d(p)/b^p
fractional part = (d(1)/b^1 + d(2)/b^2 + ... d(p-1)/b^(p-1) + d(p)/b^p)*b^p/b^p
fractional part = (d(1)*b^p/b^1 + d(2)*b^p/b^2 + ... d(p-1)*b^p/b^(p-1) + d(p)*b^p/b^p)/b^p
fractional part = (d(1)*b^(p-1) + d(2)*b^(p-2) + ... d(p-1)*b^1 + dp*b^0)/b^p
fractional part = parseInt / b^p
value = before the decimal place + after the decimal place
value = parseInt(txt before decimal) + parseInt(txt after decimal) / (base ^ length after decimal)
*/
txt = txt.split(".");
if (txt[0].length > 0) {
value = sign * parseInt(txt[0], base);
}
if (txt.length > 1 && txt[1].length > 0) {
value += sign * parseInt(txt[1], base) / Math.pow(base, txt[1].length);
}
return new BaseNumber(value, bb || base);
}
};
// Register BaseNumber with math.js
math.typed.addType({
name: "BaseNumber",
test: function (aa) {
return aa && aa.isBaseNumber;
}
});
// BaseNumber conversions for math.js
math.typed.conversions.push(
{
from: "number",
to: "BaseNumber",
convert: function (num) {
return new BaseNumber(num, 10);
}
},
/* Following causes infinite loop with 3^(\xa i) why???
{
from: "BaseNumber",
to: "number",
convert: function (aa) {
return aa.valueOf();
}
},
*/
{
from: "BaseNumber",
to: "Complex",
convert: function (aa) {
if (aa.valueOf() instanceof math.type.Complex) {
return aa.valueOf();
}
return new math.type.Complex(aa.valueOf(), 0);
}
},
{
from: "Complex",
to: "BaseNumber",
convert: function (aa) {
return new BaseNumber(aa, 10);
}
},
{
from: "string",
to: "BaseNumber",
convert: function (txt) {
return new BaseNumber(txt);
}
},
{
from: "BaseNumber",
to: "string",
convert: function (aa) {
return aa.toString();
}
},
{
from: "boolean",
to: "BaseNumber",
convert: function (bool) {
return new BaseNumber(bool, 2);
}
}
);
// BaseNumber additions to math.js library
math.import({
baseN: function (xx, yy) {
// Makes BaseNumber objects
return new BaseNumber(xx, yy);
},
hex: function (xx) {
// Makes Hexidecimal BaseNumber objects
return new BaseNumber(xx, 16);
},
oct: function (xx) {
// Makes Octadecimal BaseNumber objects
return new BaseNumber(xx, 8);
},
bin: function (xx) {
// Makes Binary BaseNumber objects
return new BaseNumber(xx, 2);
},
dec: function (xx) {
// Converts to a base10 decimal representation
if (typeof xx === "string" || xx instanceof String) {
return (new BaseNumber(xx)).valueOf();
}
if (xx.isBaseNumber) {
return xx.valueOf();
}
return xx;
},
add: math.typed("add", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.add(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.add(aa.valueOf(), bb), aa.base);
}
}),
subtract: math.typed("subtract", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.subtract(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.subtract(aa.valueOf(), bb), aa.base);
}
}),
multiply: math.typed("multiply", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.multiply(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.multiply(aa.valueOf(), bb), aa.base);
}
}),
divide: math.typed("divide", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.divide(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.divide(aa.valueOf(), bb), aa.base);
}
}),
pow: math.typed("pow", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.pow(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.pow(aa.valueOf(), bb), aa.base);
}
}),
mod: math.typed("mod", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.mod(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.mod(aa.valueOf(), bb), aa.base);
}
}),
abs: math.typed("abs", {
BaseNumber: function (aa) {
return new BaseNumber(math.abs(aa.valueOf()), aa.base);
}
}),
sqrt: math.typed("sqrt", {
BaseNumber: function (aa) {
return new BaseNumber(math.sqrt(aa.valueOf()), aa.base);
}
}),
square: math.typed("square", {
BaseNumber: function (aa) {
return new BaseNumber(math.square(aa.valueOf()), aa.base);
}
}),
cube: math.typed("cube", {
BaseNumber: function (aa) {
return new BaseNumber(math.cube(aa.valueOf()), aa.base);
}
}),
sign: math.typed("sign", {
BaseNumber: function (aa) {
return new BaseNumber(math.sign(aa.valueOf()), aa.base);
}
}),
unaryMinus: math.typed("unaryMinus", {
BaseNumber: function (aa) {
return new BaseNumber(math.unaryMinus(aa.valueOf()), aa.base);
}
}),
unaryPlus: math.typed("unaryPlus", {
BaseNumber: function (aa) {
return new BaseNumber(math.unaryPlus(aa.valueOf()), aa.base);
}
}),
bitNot: math.typed("bitNot", {
BaseNumber: function (aa) {
return new BaseNumber(math.bitNot(aa.valueOf()), aa.base);
}
}),
bitAnd: math.typed("bitAnd", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.bitAnd(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.bitAnd(aa.valueOf(), bb), aa.base);
}
}),
bitOr: math.typed("bitOr", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.bitOr(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.bitOr(aa.valueOf(), bb), aa.base);
}
}),
bitXor: math.typed("bitXor", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.bitXor(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.bitXor(aa.valueOf(), bb), aa.base);
}
}),
leftShift: math.typed("leftShift", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.leftShift(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.leftShift(aa.valueOf(), bb), aa.base);
}
}),
rightArithShift: math.typed("rightArithShift", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.rightArithShift(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.rightArithShift(aa.valueOf(), bb), aa.base);
}
}),
rightLogShift: math.typed("rightLogShift", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.rightLogShift(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.rightLogShift(aa.valueOf(), bb), aa.base);
}
}),
not: math.typed("not", {
BaseNumber: function (aa) {
return new BaseNumber(math.not(aa.valueOf()), aa.base);
}
}),
and: math.typed("and", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.and(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.and(aa.valueOf(), bb), aa.base);
}
}),
or: math.typed("or", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.or(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.or(aa.valueOf(), bb), aa.base);
}
}),
xor: math.typed("xor", {
"BaseNumber, BaseNumber | number": function (aa, bb) {
return new BaseNumber(math.xor(aa.valueOf(), bb.valueOf()), aa.base);
},
"BaseNumber, Complex": function (aa, bb) {
return new BaseNumber(math.xor(aa.valueOf(), bb), aa.base);
}
})
});
// Add documentation for base-N numbers to Math.js
math.expression.docs.hex = {
name: "hex",
category: "Base-N Numbers",
description: "Creates a real base-16 hexadecimal number from a string or another number",
syntax: ["hex(string)", "hex(number)"],
examples: ['hex("xAF.F")', "hex(500)"],
seealso: ["bin", "oct", "dec", "baseN"]
};
math.expression.docs.oct = {
name: "oct",
category: "Base-N Numbers",
description: "Creates a real base-8 number from a string or another number",
syntax: ["oct(string)", "oct(number)"],
examples: ['oct("o76.05")', "oct(\\b1001)"],
seealso: ["bin", "oct", "dec", "baseN"]
};
math.expression.docs.bin = {
name: "bin",
category: "Base-N Numbers",
description: "Creates a real base-2 binary number from a string or another number",
syntax: ["bin(string)", "bin(number)"],
examples: ['bin("b1001.11")', "bin(500)"],
seealso: ["bin", "oct", "dec", "baseN"]
};
math.expression.docs.dec = {
name: "dec",
category: "Base-N Numbers",
description: "Converts a base-N number to a base-10 number",
syntax: ["dec(string)", "dec(number)"],
examples: ['dec("12345.06789")', "dec(500)"],
seealso: ["bin", "oct", "dec", "baseN"]
};
math.expression.docs.baseN = {
name: "baseN",
category: "Base-N Numbers",
description: "Creates a real base-N number from a string or another number",
syntax: ["baseN(string)", "baseN(number, base)"],
examples: ['baseN("xAF.F")', "baseN(500,3)"],
seealso: ["bin", "oct", "dec", "baseN"]
};
}()); |
Thanks for sharing your solution @Frodojj |
Has this been implemented in mathjs? I would like to use Is that possible? Thanks. |
@q2apro this hasn't been implemented yet. |
Support for hex, bin, and oct numbers in the expression parser is now available in We have plans to extend this further, but I'll close this issue now since there is a working version now. |
@josdejong mentioned he wants to implement this.
I thought it will be good to have the card for this anyway.
Also here is the code I use and you can find useful:
then you can define the following functions in math.js
function bin(x) = binBase(x, 2)
function oct(x) = binBase(x, 8)
function dec(x,y) = binBase(x, 10, y)
function hex(x) = binBase(x, 16)
function base(x,y,z) = binBase(x, y, z)
Above we prefix binary numbers with 0b and hex numbers with 0x. (was using 0 prefix for octal numbers, but that was confusing. Still in search for a better notation for them. )
The text was updated successfully, but these errors were encountered: