Current answers are either:
- suggesting huge external libraries
- using
eval('...')
orFunction('...')
which is bad - working only with
+
,-
- failing at operator precedence (e.g
1+2*3
should return 7 not 9) when they implement*
and/or/
Here an implementation (270 lines of code) that replaces eval
.
It supports +
, -
, *
, /
, %
, ^
, parentheses and functions (min, max, sin, cos, tan, log). You can also easily add support for more functions like sqrt, asin, acos…)
It uses TypeScript, is documented and tested.
It internally uses the shunting yard algorithm and reverse Polish notation.
// WTF!
// parseFloat('-0') => -0 vs parseFloat(-0) => 0
// -0 === 0 => true vs Object.is(-0, 0) => false
const minus0Hack = (value: number) => (Object.is(value, -0) ? '-0' : value);
export const operators: {
[operator: string]:
| {
func: (...args: string[]) => string;
precedence: number;
associativity: 'left' | 'right';
arity: number; // Needed by evalReversePolishNotation()
}
| undefined;
} = {
'+': {
func: (x, y) => `${minus0Hack(Number(x) + Number(y))}`,
precedence: 1,
associativity: 'left',
arity: 2
},
'-': {
func: (x, y) => `${minus0Hack(Number(x) - Number(y))}`,
precedence: 1,
associativity: 'left',
arity: 2
},
'*': {
func: (x, y) => `${minus0Hack(Number(x) * Number(y))}`,
precedence: 2,
associativity: 'left',
arity: 2
},
'/': {
func: (x, y) => `${minus0Hack(Number(x) / Number(y))}`,
precedence: 2,
associativity: 'left',
arity: 2
},
'%': {
func: (x, y) => `${minus0Hack(Number(x) % Number(y))}`,
precedence: 2,
associativity: 'left',
arity: 2
},
'^': {
// Why Math.pow() instead of **?
// -2 ** 2 => "SyntaxError: Unary operator used immediately before exponentiation expression..."
// Math.pow(-2, 2) => -4
// eslint-disable-next-line prefer-exponentiation-operator, no-restricted-properties
func: (x, y) => `${minus0Hack(Math.pow(Number(x), Number(y)))}`,
precedence: 3,
associativity: 'right',
arity: 2
}
};
export const operatorsKeys = Object.keys(operators);
export const functions: {
[operator: string]:
| {
func: (...args: string[]) => string;
// Needed by evalReversePolishNotation()
arity: number;
}
| undefined;
} = {
min: { func: (x, y) => `${minus0Hack(Math.min(Number(x), Number(y)))}`, arity: 2 },
max: { func: (x, y) => `${minus0Hack(Math.max(Number(x), Number(y)))}`, arity: 2 },
sin: { func: x => `${minus0Hack(Math.sin(Number(x)))}`, arity: 1 },
cos: { func: x => `${minus0Hack(Math.cos(Number(x)))}`, arity: 1 },
tan: { func: x => `${minus0Hack(Math.tan(Number(x)))}`, arity: 1 },
log: { func: x => `${Math.log(Number(x))}`, arity: 1 } // No need for -0 hack
};
export const functionsKeys = Object.keys(functions);
const top = (stack: string[]): string | undefined => stack[stack.length - 1];
/**
* Shunting yard algorithm: converts infix expression to postfix expression (reverse Polish notation)
*
* Example: ['1', '+', '2'] => ['1', '2', '+']
*
* https://en.wikipedia.org/wiki/Shunting_yard_algorithm
* https://github.com/poteat/shunting-yard-typescript
* https://blog.kallisti.net.nz/2008/02/extension-to-the-shunting-yard-algorithm-to-allow-variable-numbers-of-arguments-to-functions/
*/
export function shuntingYard(tokens: string[]) {
const output = new Array<string>();
const operatorStack = new Array<string>();
for (const token of tokens) {
if (functions[token] !== undefined) {
operatorStack.push(token);
} else if (token === ',') {
while (operatorStack.length > 0 && top(operatorStack) !== '(') {
output.push(operatorStack.pop()!);
}
if (operatorStack.length === 0) {
throw new Error("Misplaced ','");
}
} else if (operators[token] !== undefined) {
const o1 = token;
while (
operatorStack.length > 0 &&
top(operatorStack) !== undefined &&
top(operatorStack) !== '(' &&
(operators[top(operatorStack)!]!.precedence > operators[o1]!.precedence ||
(operators[o1]!.precedence === operators[top(operatorStack)!]!.precedence &&
operators[o1]!.associativity === 'left'))
) {
output.push(operatorStack.pop()!); // o2
}
operatorStack.push(o1);
} else if (token === '(') {
operatorStack.push(token);
} else if (token === ')') {
while (operatorStack.length > 0 && top(operatorStack) !== '(') {
output.push(operatorStack.pop()!);
}
if (operatorStack.length > 0 && top(operatorStack) === '(') {
operatorStack.pop();
} else {
throw new Error('Parentheses mismatch');
}
if (functions[top(operatorStack)!] !== undefined) {
output.push(operatorStack.pop()!);
}
} else {
output.push(token);
}
}
// Remaining items
while (operatorStack.length > 0) {
const operator = top(operatorStack);
if (operator === '(') {
throw new Error('Parentheses mismatch');
} else {
output.push(operatorStack.pop()!);
}
}
return output;
}
/**
* Evaluates reverse Polish notation (RPN) (postfix expression).
*
* Example: ['1', '2', '+'] => 3
*
* https://en.wikipedia.org/wiki/Reverse_Polish_notation
* https://github.com/poteat/shunting-yard-typescript
*/
export function evalReversePolishNotation(tokens: string[]) {
const stack = new Array<string>();
const ops = { ...operators, ...functions };
for (const token of tokens) {
const op = ops[token];
// eslint-disable-next-line unicorn/no-negated-condition
if (op !== undefined) {
const parameters = [];
for (let i = 0; i < op.arity; i++) {
parameters.push(stack.pop()!);
}
stack.push(op.func(...parameters.reverse()));
} else {
stack.push(token);
}
}
if (stack.length > 1) {
throw new Error('Insufficient operators');
}
return Number(stack[0]);
}
/**
* Breaks a mathematical expression into tokens.
*
* Example: "1 + 2" => [1, '+', 2]
*
* https://gist.github.com/tchayen/44c28e8d4230b3b05e9f
*/
export function tokenize(expression: string) {
// "1 +" => "1 +"
const expr = expression.replace(/s+/g, ' ');
const tokens = [];
let acc = '';
let currentNumber = '';
for (let i = 0; i < expr.length; i++) {
const c = expr.charAt(i);
const prev_c = expr.charAt(i - 1); // '' if index out of range
const next_c = expr.charAt(i + 1); // '' if index out of range
const lastToken = top(tokens);
const numberParsingStarted = currentNumber !== '';
if (
// 1
/d/.test(c) ||
// Unary operator: +1 or -1
((c === '+' || c === '-') &&
!numberParsingStarted &&
(lastToken === undefined ||
lastToken === ',' ||
lastToken === '(' ||
operatorsKeys.includes(lastToken)) &&
/d/.test(next_c))
) {
currentNumber += c;
} else if (c === '.') {
if (numberParsingStarted && currentNumber.includes('.')) {
throw new Error(`Double '.' in number: '${currentNumber}${c}'`);
} else {
currentNumber += c;
}
} else if (c === ' ') {
if (/d/.test(prev_c) && /d/.test(next_c)) {
throw new Error(`Space in number: '${currentNumber}${c}${next_c}'`);
}
} else if (functionsKeys.includes(acc + c)) {
acc += c;
if (!functionsKeys.includes(acc + next_c)) {
tokens.push(acc);
acc = '';
}
} else if (operatorsKeys.includes(c) || c === '(' || c === ')' || c === ',') {
if (
operatorsKeys.includes(c) &&
!numberParsingStarted &&
operatorsKeys.includes(lastToken!)
) {
throw new Error(`Consecutive operators: '${lastToken!}${c}'`);
}
if (numberParsingStarted) {
tokens.push(currentNumber);
}
tokens.push(c);
currentNumber = '';
} else {
acc += c;
}
}
if (acc !== '') {
throw new Error(`Invalid characters: '${acc}'`);
}
// Add last number to the tokens
if (currentNumber !== '') {
tokens.push(currentNumber);
}
// ['+', '1'] => ['0', '+', '1']
// ['-', '1'] => ['0', '-', '1']
if (tokens[0] === '+' || tokens[0] === '-') {
tokens.unshift('0');
}
return tokens;
}
export function calculate(expression: string) {
const tokens = tokenize(expression);
const rpn = shuntingYard(tokens);
return evalReversePolishNotation(rpn);
}
The most important part, the unit tests:
/* eslint-disable no-eval, unicorn/prefer-number-properties */
import { calculate, evalReversePolishNotation, shuntingYard, tokenize } from './calculator';
import { convertMathExpressionToEval, getRandomMathExpression } from './getRandomMathExpression';
import { getRandomInt } from './getRandomNumber';
test('shuntingYard()', () => {
{
// https://en.wikipedia.org/wiki/Shunting_yard_algorithm#Detailed_examples
const rpn = shuntingYard('3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3'.split(' '));
expect(rpn).toEqual(['3', '4', '2', '*', '1', '5', '-', '2', '3', '^', '^', '/', '+']);
}
{
// https://en.wikipedia.org/wiki/Shunting_yard_algorithm#Detailed_examples
const rpn = shuntingYard('sin ( max ( 2 3 ) / 3 * 3.14 )'.split(' '));
expect(rpn).toEqual(['2', '3', 'max', '3', '/', '3.14', '*', 'sin']);
}
// Parentheses mismatch
expect(() => shuntingYard(['('])).toThrow('Parentheses mismatch');
expect(() => shuntingYard([')'])).toThrow('Parentheses mismatch');
expect(() => shuntingYard('1 - ( 2 * 3 ) )'.split(' '))).toThrow('Parentheses mismatch');
expect(() => shuntingYard('1 - ( 2 * 3 ) ) + 4'.split(' '))).toThrow('Parentheses mismatch');
// Ignore ','
expect(shuntingYard('max ( 1 , 2 )'.split(' '))).toEqual(['1', '2', 'max']);
// if the token is: ',':
// while the operator at the top of the operator stack is not a left parenthesis:
// pop the operator from the operator stack into the output queue
expect(shuntingYard('max ( 0 + 1 , 2 )'.split(' '))).toEqual(['0', '1', '+', '2', 'max']);
// Misplaced ','
expect(() => shuntingYard('1 , 2'.split(' '))).toThrow("Misplaced ','");
expect(() => shuntingYard(', 1 / 2'.split(' '))).toThrow("Misplaced ','");
expect(() => shuntingYard('1 , / 2'.split(' '))).toThrow("Misplaced ','");
expect(() => shuntingYard('1 / , 2'.split(' '))).toThrow("Misplaced ','");
expect(() => shuntingYard('1 / 2 ,'.split(' '))).toThrow("Misplaced ','");
expect(() =>
shuntingYard('sin ( , max , ( , 2 , 3 , ) , / , 3 , * , 3.14 , )'.split(' '))
).not.toThrow();
// Edge cases
expect(shuntingYard([''])).toEqual(['']);
expect(shuntingYard([' '])).toEqual([' ']);
expect(shuntingYard(['1'])).toEqual(['1']);
expect(shuntingYard(['a'])).toEqual(['a']);
expect(shuntingYard(['1a'])).toEqual(['1a']);
expect(shuntingYard(['*'])).toEqual(['*']);
expect(shuntingYard(['/'])).toEqual(['/']);
// All together expression
expect(
shuntingYard(
'( ( 3.1 + cos ( -4 ) / 2 ) * max ( -6 , 6 ) ^ sin ( 6 ) * 9 ) / tan ( log ( 8.8 + -2 ) % 7 ) + ( 6 * -1 - min ( 6 , -4.2 ) )'.split(
' '
)
)
).toEqual(
'3.1 -4 cos 2 / + -6 6 max 6 sin ^ * 9 * 8.8 -2 + log 7 % tan / 6 -1 * 6 -4.2 min - +'.split(
' '
)
);
});
test('reversePolishNotation()', () => {
// https://rosettacode.org/wiki/Parsing/RPN_calculator_algorithm#JavaScript
expect(
evalReversePolishNotation(['3', '4', '2', '*', '1', '5', '-', '2', '3', '^', '^', '/', '+'])
).toEqual(3 + (4 * 2) / (1 - 5) ** (2 ** 3));
expect(
evalReversePolishNotation(['3', '4', '2', '*', '1', '5', '-', '2', '3', '^', '^', '/', '+'])
).toEqual(3.000_122_070_312_5);
// https://en.wikipedia.org/wiki/Shunting_yard_algorithm#Detailed_examples
expect(evalReversePolishNotation(['2', '3', 'max', '3', '/', '3.14', '*', 'sin'])).toEqual(
Math.sin((Math.max(2, 3) / 3) * 3.14)
);
expect(evalReversePolishNotation(['2', '3', 'max', '3', '/', '3.14', '*', 'sin'])).toEqual(
0.001_592_652_916_486_828_2
);
// Edge cases
expect(evalReversePolishNotation([''])).toEqual(0); // :-(
expect(evalReversePolishNotation([' '])).toEqual(0); // :-(
expect(evalReversePolishNotation(['1'])).toEqual(1);
expect(evalReversePolishNotation(['a'])).toBeNaN();
expect(evalReversePolishNotation(['1a'])).toBeNaN();
expect(evalReversePolishNotation(['*'])).toBeNaN();
expect(evalReversePolishNotation(['/'])).toBeNaN();
expect(() => evalReversePolishNotation(['1', '2'])).toThrow('Insufficient operators');
// All together expression
expect(
evalReversePolishNotation(
'3.1 -4 cos 2 / + -6 6 max 6 sin ^ * 9 * 8.8 -2 + log 7 % tan / 6 -1 * 6 -4.2 min - +'.split(
' '
)
)
).toEqual(
eval(
'((3.1 + Math.cos(-4) / 2) * Math.max(-6, 6) ** Math.sin(6) * 9) / Math.tan(Math.log(8.8 + -2) % 7) + (6 * -1 - Math.min(6, -4.2))'
)
);
});
test('tokenize()', () => {
// https://en.wikipedia.org/wiki/Shunting_yard_algorithm#Detailed_examples
expect(tokenize('3 + 4 * 2 / (1 - 5) ^ 2 ^ 3')).toEqual(
'3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3'.split(' ')
);
// https://en.wikipedia.org/wiki/Shunting_yard_algorithm#Detailed_examples
expect(tokenize('sin(max(2, 3) / 3 * 3.14)')).toEqual(
'sin ( max ( 2 , 3 ) / 3 * 3.14 )'.split(' ')
);
expect(tokenize('1+2')).toEqual(['1', '+', '2']);
expect(tokenize('min(1,2)')).toEqual(['min', '(', '1', ',', '2', ')']);
expect(tokenize('1.1+2.2')).toEqual(['1.1', '+', '2.2']);
expect(tokenize('min(1.1,2.2)')).toEqual(['min', '(', '1.1', ',', '2.2', ')']);
// Decimals
expect(tokenize('1.1 + 2.2 - 3.3 * 4.4 / 5.5 % 6.6 ^ 7.7')).toEqual(
'1.1 + 2.2 - 3.3 * 4.4 / 5.5 % 6.6 ^ 7.7'.split(' ')
);
// White spaces
expect(tokenize('')).toEqual([]);
expect(tokenize(' ')).toEqual([]);
expect(tokenize(' 1 + 2 ')).toEqual(['1', '+', '2']);
expect(tokenize('1 n + n 2')).toEqual(['1', '+', '2']);
expect(tokenize('1 t + t 2')).toEqual(['1', '+', '2']);
// Single number
expect(tokenize('0')).toEqual(['0']);
expect(tokenize('1')).toEqual(['1']);
expect(tokenize('-0')).toEqual(['-0']);
expect(tokenize('-1')).toEqual(['-1']);
expect(tokenize('(1)')).toEqual(['(', '1', ')']);
expect(tokenize('(-1)')).toEqual(['(', '-1', ')']);
expect(tokenize('-(1)')).toEqual(['0', '-', '(', '1', ')']);
// Starting with +/-
expect(tokenize('+0')).toEqual(['+0']);
expect(tokenize('+ 0')).toEqual(['0', '+', '0']);
expect(tokenize('-0')).toEqual(['-0']);
expect(tokenize('- 0')).toEqual(['0', '-', '0']);
expect(tokenize('+1')).toEqual(['+1']);
expect(tokenize('+ 1')).toEqual(['0', '+', '1']);
expect(tokenize('-1')).toEqual(['-1']);
expect(tokenize('- 1')).toEqual(['0', '-', '1']);
expect(tokenize('+1 + 1')).toEqual(['+1', '+', '1']);
expect(tokenize('+ 1 + 1')).toEqual(['0', '+', '1', '+', '1']);
expect(tokenize('-1 + 1')).toEqual(['-1', '+', '1']);
expect(tokenize('- 1 + 1')).toEqual(['0', '-', '1', '+', '1']);
expect(tokenize('+')).toEqual(['0', '+']);
expect(tokenize('-')).toEqual(['0', '-']);
// Do not confuse '+1' / '-1' with 'x + 1' / 'x - 1' depending on the context
expect(tokenize('(1+2)+1')).toEqual(['(', '1', '+', '2', ')', '+', '1']);
expect(tokenize('(1+2)-1')).toEqual(['(', '1', '+', '2', ')', '-', '1']);
expect(tokenize('1 + -2')).toEqual(['1', '+', '-2']);
expect(tokenize('1+-2')).toEqual(['1', '+', '-2']);
// Space in number
expect(() => tokenize('1 2')).toThrow("Space in number: '1 2'");
expect(() => tokenize('1 2')).toThrow("Space in number: '1 2'");
expect(() => tokenize('0 + 1 / (2 3) * 4')).toThrow("Space in number: '2 3'");
expect(() => tokenize('min(1 2)')).toThrow("Space in number: '1 2'");
// Double '.' in number
expect(() => tokenize('1+2.3.4')).toThrow("Double '.' in number: '2.3.'");
expect(() => tokenize('1+2.3.4.5')).toThrow("Double '.' in number: '2.3.'");
expect(() => tokenize('0 + 1 / 2.3.4 * 5')).toThrow("Double '.' in number: '2.3.'");
expect(() => tokenize('min(1, 2.3.4)')).toThrow("Double '.' in number: '2.3.'");
// Consecutive operators
expect(tokenize('1++2')).toEqual(['1', '+', '+2']);
expect(tokenize('1-+2')).toEqual(['1', '-', '+2']);
expect(tokenize('1--2')).toEqual(['1', '-', '-2']);
expect(() => tokenize('1++')).toThrow("Consecutive operators: '++'");
expect(() => tokenize('1-+')).toThrow("Consecutive operators: '-+'");
expect(() => tokenize('1--')).toThrow("Consecutive operators: '--'");
expect(() => tokenize('1-*2')).toThrow("Consecutive operators: '-*'");
expect(() => tokenize('0 + 1 / (2-*3) * 4')).toThrow("Consecutive operators: '-*'");
expect(() => tokenize('min(1-*2, 3)')).toThrow("Consecutive operators: '-*'");
// Other edge cases
expect(tokenize('1,2')).toEqual(['1', ',', '2']);
expect(tokenize('1+2+')).toEqual(['1', '+', '2', '+']); // :-(
expect(() => tokenize('1+2a')).toThrow("Invalid characters: 'a'");
expect(() => tokenize('10 Hello')).toThrow("Invalid characters: 'Hello'");
expect(tokenize('1-.')).toEqual(['1', '-', '.']); // :-(
expect(tokenize('*')).toEqual(['*']);
expect(tokenize('/')).toEqual(['/']);
// All together expression
expect(
tokenize(
'((3.1 + cos(-4) / 2) * max(-6, 6) ^ sin(6) * 9) / tan(log(8.8 + -2) % 7) + (6 * -1 - min(6, -4.2))'
)
).toEqual(
'( ( 3.1 + cos ( -4 ) / 2 ) * max ( -6 , 6 ) ^ sin ( 6 ) * 9 ) / tan ( log ( 8.8 + -2 ) % 7 ) + ( 6 * -1 - min ( 6 , -4.2 ) )'.split(
' '
)
);
expect(
tokenize('((3.1+cos(-4)/2)*max(-6,6)^sin(6)*9)/tan(log(8.8+-2)%7)+(6*-1-min(6,-4.2))')
).toEqual(
'( ( 3.1 + cos ( -4 ) / 2 ) * max ( -6 , 6 ) ^ sin ( 6 ) * 9 ) / tan ( log ( 8.8 + -2 ) % 7 ) + ( 6 * -1 - min ( 6 , -4.2 ) )'.split(
' '
)
);
});
test('calculate()', () => {
// https://en.wikipedia.org/wiki/Shunting_yard_algorithm#Detailed_examples
expect(calculate('3 + 4 * 2 / (1 - 5) ^ 2 ^ 3')).toEqual(3.000_122_070_312_5);
// https://en.wikipedia.org/wiki/Shunting_yard_algorithm#Detailed_examples
expect(calculate('sin(max(2, 3) / 3 * 3.14)')).toEqual(0.001_592_652_916_486_828_2);
expect(calculate('1+2')).toEqual(3);
expect(calculate('min(1,2)')).toEqual(1);
expect(calculate('1.1+2.2')).toEqual(3.300_000_000_000_000_3);
expect(calculate('min(1.1,2.2)')).toEqual(1.1);
// if the token is: ',':
// while the operator at the top of the operator stack is not a left parenthesis:
// pop the operator from the operator stack into the output queue
expect(calculate('max(0 + 1, 2)')).toEqual(2);
// Decimals
expect(calculate('1.1 + 2.2 - 3.3 * 4.4 / 5.5 % 6.6 ^ 7.7')).toEqual(
eval('1.1 + 2.2 - 3.3 * 4.4 / 5.5 % 6.6 ** 7.7')
);
// White spaces
expect(calculate('')).toBeNaN();
expect(calculate(' ')).toBeNaN();
expect(calculate(' 1 + 2 ')).toEqual(3);
expect(calculate('1 n + n 2')).toEqual(3);
expect(calculate('1 t + t 2')).toEqual(3);
// -0 hack
expect(calculate('-0 + -0')).toEqual(-0);
expect(calculate('-0 - 0')).toEqual(-0);
expect(calculate('0 * -1')).toEqual(-0);
expect(calculate('0 / -1')).toEqual(-0);
expect(calculate('-1 % 1')).toEqual(-0);
expect(calculate('-0 ^ 1')).toEqual(-0);
expect(calculate('min(-0, 1)')).toEqual(-0);
expect(calculate('max(-0, -1)')).toEqual(-0);
expect(calculate('sin(-0)')).toEqual(-0);
//expect(Math.cos(Math.PI / 2)).toEqual(0);
expect(calculate('tan(-0)')).toEqual(-0);
expect(calculate('log(1)')).toEqual(0); // No need for -0 hack
// Math.pow() vs **
expect(calculate('-2 ^ 2')).toEqual((-2) ** 2);
expect(eval('Math.pow(-2, 2)')).toEqual(4);
expect(() => eval('-2 ** 2')).toThrow(
'Unary operator used immediately before exponentiation expression.'
);
// Infinity/-Infinity
expect(calculate('1 / 0')).toEqual(Infinity);
expect(calculate('1 / -0')).toEqual(-Infinity);
expect(calculate('-1 / 0')).toEqual(-Infinity);
expect(calculate('1 + 1 / 0')).toEqual(Infinity);
expect(calculate('1 - 1 / 0')).toEqual(-Infinity);
expect(calculate('10 ^ 1000')).toEqual(Infinity);
expect(calculate('0 - 10 ^ 1000')).toEqual(-Infinity);
expect(calculate('0 ^ -1')).toEqual(Infinity);
expect(calculate('-0 ^ -1')).toEqual(-Infinity);
expect(calculate('log(0)')).toEqual(-Infinity);
// NaN
expect(calculate('log(-1)')).toBeNaN();
expect(calculate('-1 ^ 0.1')).toBeNaN();
expect(calculate('1 % 0')).toBeNaN();
expect(calculate('1 / 0 * 0')).toBeNaN();
// Single number
expect(calculate('0')).toEqual(0);
expect(calculate('1')).toEqual(1);
expect(calculate('-0')).toEqual(-0);
expect(calculate('-1')).toEqual(-1);
expect(calculate('(1)')).toEqual(1);
expect(calculate('(-1)')).toEqual(-1);
expect(calculate('-(1)')).toEqual(-1);
// Starting with +/-
expect(calculate('+0')).toEqual(0);
expect(calculate('+ 0')).toEqual(0);
expect(calculate('-0')).toEqual(-0);
expect(calculate('- 0')).toEqual(0);
expect(calculate('+1')).toEqual(1);
expect(calculate('+ 1')).toEqual(+1);
expect(calculate('-1')).toEqual(-1);
expect(calculate('- 1')).toEqual(-1);
expect(calculate('+1 + 1')).toEqual(2);
expect(calculate('+ 1 + 1')).toEqual(2);
expect(calculate('-1 + 1')).toEqual(0);
expect(calculate('- 1 + 1')).toEqual(0);
expect(calculate('+')).toBeNaN();
expect(calculate('-')).toBeNaN();
// Do not confuse '+1' / '-1' with 'x + 1' / 'x - 1' depending on the context
expect(calculate('(1+2)+1')).toEqual(4);
expect(calculate('(1+2)-1')).toEqual(2);
expect(calculate('1 + -2')).toEqual(-1);
expect(calculate('1+-2')).toEqual(-1);
// Space in number
expect(() => calculate('1 2')).toThrow("Space in number: '1 2'");
expect(() => calculate('1 2')).toThrow("Space in number: '1 2'");
expect(() => calculate('0 + 1 / (2 3) * 4')).toThrow("Space in number: '2 3'");
expect(() => calculate('min(1 2)')).toThrow("Space in number: '1 2'");
// Double '.' in number
expect(() => calculate('1+2.3.4')).toThrow("Double '.' in number: '2.3.'");
expect(() => calculate('1+2.3.4.5')).toThrow("Double '.' in number: '2.3.'");
expect(() => calculate('0 + 1 / 2.3.4 * 5')).toThrow("Double '.' in number: '2.3.'");
expect(() => calculate('min(1, 2.3.4)')).toThrow("Double '.' in number: '2.3.'");
// Consecutive operators
expect(calculate('1++2')).toEqual(3);
expect(calculate('1-+2')).toEqual(-1);
expect(calculate('1--2')).toEqual(3);
expect(() => calculate('1++')).toThrow("Consecutive operators: '++'");
expect(() => calculate('1-+')).toThrow("Consecutive operators: '-+'");
expect(() => calculate('1--')).toThrow("Consecutive operators: '--'");
expect(() => calculate('1-*2')).toThrow("Consecutive operators: '-*'");
expect(() => calculate('0 + 1 / (2-*3) * 4')).toThrow("Consecutive operators: '-*'");
expect(() => calculate('min(1-*2, 3)')).toThrow("Consecutive operators: '-*'");
// Misplaced ','
expect(() => calculate('1,2')).toThrow("Misplaced ','");
expect(() => calculate(',1/2')).toThrow("Misplaced ','");
expect(() => calculate('1,/2')).toThrow("Misplaced ','");
expect(() => calculate('1/,2')).toThrow("Misplaced ','");
expect(() => calculate('1/2,')).toThrow("Misplaced ','");
expect(() => calculate('sin(,max,(,2,3,),/,3,*,3.14,)')).toThrow('Insufficient operators');
expect(calculate('sin(,max(,2,3,),/3,*3.14,)')).toEqual(0.001_592_652_916_486_828_2);
// Other edge cases
expect(calculate('1+2+')).toBeNaN();
expect(() => calculate('1+2a')).toThrow("Invalid characters: 'a'");
expect(() => calculate('10 Hello')).toThrow("Invalid characters: 'Hello'");
expect(calculate('1-.')).toBeNaN();
expect(calculate('*')).toBeNaN();
expect(calculate('/')).toBeNaN();
// All together expression
expect(
calculate(
'((3.1 + cos(-4) / 2) * max(-6, 6) ^ sin(6) * 9) / tan(log(8.8 + -2) % 7) + (6 * -1 - min(6, -4.2))'
)
).toEqual(
eval(
'((3.1 + Math.cos(-4) / 2) * Math.max(-6, 6) ** Math.sin(6) * 9) / Math.tan(Math.log(8.8 + -2) % 7) + (6 * -1 - Math.min(6, -4.2))'
)
);
expect(
calculate('((3.1+cos(-4)/2)*max(-6,6)^sin(6)*9)/tan(log(8.8+-2)%7)+(6*-1-min(6,-4.2))')
).toEqual(
eval(
'((3.1+Math.cos(-4)/2)*Math.max(-6,6)**Math.sin(6)*9)/Math.tan(Math.log(8.8+-2)%7)+(6*-1-Math.min(6,-4.2))'
)
);
});
test('calculate() with getRandomMathExpression()', () => {
for (let i = 0; i < 1000; i++) {
const expr = getRandomMathExpression(getRandomInt(1, 100));
expect(calculate(expr)).toEqual(eval(convertMathExpressionToEval(expr)));
}
});
More here: https://gist.github.com/tkrotoff/b0b1d39da340f5fc6c5e2a79a8b6cec0
Another solution that only supports +
, -
, *
, /
without parentheses: https://code.tutsplus.com/tutorials/what-they-didnt-tell-you-about-es5s-array-extras–net-28263 (by Felix Bohm)
function calculate(expression: string) {
const parts = parse(expression); // Tokenize function to be implemented
// Build an array with all operations reduced to additions
const processed = new Array<number>();
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
switch (part) {
case '+': {
// Ignore
break;
}
case '-': {
const rightValue = parts[++i];
if (typeof rightValue === 'number') {
processed.push(-1 * rightValue);
} else {
processed.push(Number.NaN);
}
break;
}
case '*': {
const leftValue = processed.pop();
const rightValue = parts[++i];
if (typeof leftValue === 'number' && typeof rightValue === 'number') {
processed.push(leftValue * rightValue);
} else {
processed.push(Number.NaN);
}
break;
}
case '/': {
const leftValue = processed.pop();
const rightValue = parts[++i];
if (typeof leftValue === 'number' && typeof rightValue === 'number') {
processed.push(leftValue / rightValue);
} else {
processed.push(Number.NaN);
}
break;
}
default: {
processed.push(part);
}
}
}
// Add all numbers and return the result
return processed.reduce((accumulator, currentValue) => accumulator + currentValue);
}
Как можно вычислять значение математической функции представленную в виде строки?
Например: f(x,y) = 2*x + 5/y
Как можно подставить x и y, затем вычислить значение функции,
задан 8 дек 2018 в 14:06
var f = "f(x,y) = 2*x + 5/y";
var x = 100;
var y = 2.5;
f = f.replace(/f(x,y)s=s/g, ""); //удаляем лишнее
f = f.replace(/x/g, x); // заменяем подстроку x на значение переменной x
f = f.replace(/y/g, y); // тоже самое с y
f = eval(f); // вычисляем значение строкового выражения
console.log(f);
ответ дан 8 дек 2018 в 14:25
Misha SaidovMisha Saidov
5,3761 золотой знак9 серебряных знаков30 бронзовых знаков
2
Нашел отличную библиотеку для решения задачи “math.js”:
var f = "2*x^2 + sqrt(5/y)";
var fx = 2;
var fy = 5
var node = math.parse(f);
console.log(node.eval({x: fx, y: fy}));
ответ дан 8 дек 2018 в 16:08
elossaelossa
1491 золотой знак2 серебряных знака14 бронзовых знаков
Встроенный объект JavaScript Math включает ряд полезных функций для выполнения различных математических операций. Давайте погрузимся и посмотрим, как они работают и для чего вы можете их использовать.
Содержание
- Math.max и Math.min
- Абсолютные значения
- Math.pow
- Вычисление корней
- Логарифмы и экспоненты
- Гипотенуза
Math.max и Math.min
Эти функции в значительной степени делают то, что вы ожидаете: они возвращают максимум или минимум списка предоставленных аргументов:
Math.max(1,2,3,4,5) << 5 Math.min(4,71,-7,2,1,0) << -7
Все аргументы должны быть типа Number данных. В противном случае NaNбудет возвращено:
Math.max('a','b','c') << NaN Math.min(5,"hello",6) << NaN
Однако будьте осторожны. JavaScript попытается привести значения к числу:
Math.min(5,true,6) << 1
В этом примере логическое значение trueпреобразуется в число 1, поэтому оно возвращается как минимальное значение. Если вы не знакомы с приведением типов, это происходит, когда операнды оператора относятся к разным типам. В этом случае JavaScript попытается преобразовать один операнд в эквивалентное значение типа другого операнда.
В качестве аргумента необходимо указать список чисел, а не массив, но вы можете использовать оператор расширения (…) для распаковки массива чисел:
Math.max(...[8,4,2,1]) << 8
Функция Math.maxполезна для поиска рекорда из списка результатов, сохраненных в массиве:
const scores = [23,12,52,6,25,38,19,37,76,54,24] const highScore = Math.max(...scores) << 76
Функция Math.minполезна для поиска лучшей цены на сайте сравнения цен:
const prices = [19.99, 20.25, 18.57, 19,75, 25, 22.50] const bestPrice = Math.min(...prices) << 18.57
Абсолютные значения
Абсолютное значение — это просто размер числа, независимо от его размера. Это означает, что положительные числа остаются прежними, а отрицательные теряют знак минус. Функция Math.absвычислит абсолютное значение своего аргумента:
Math.abs(5) << 5 Math.abs(-42) << 42 Math.abs(-3.14159) << 3.14159
Почему вы хотите это сделать? Ну, иногда вы хотите вычислить разницу между двумя значениями, которую вы вычисляете, вычитая наименьшее из наибольшего, но часто вы не будете знать заранее, какое из двух значений наименьшее. Чтобы обойти это, вы можете просто вычесть числа в любом порядке и взять абсолютное значение:
const x = 5 const y = 8 const difference = Math.abs(x - y) << 3
Практический пример может быть на веб-сайте для экономии денег, где вы хотите знать, сколько вы можете сэкономить, рассчитав разницу между двумя сделками, поскольку вы будете иметь дело с данными о ценах в реальном времени и не будете знать заранее, какая сделка была. самый дешевый:
const dealA = 150 const dealB = 167 const saving = Math.abs(dealA - dealB) << 17
Math.pow
Math.powвыполняет расчеты мощности, например:
3⁴ = 81
В приведенном выше примере число 3 известно как основание, а 4 — как показатель степени. Мы бы прочитали это как «3 в степени 4 равно 81».
Функция принимает два значения — основание и показатель степени — и возвращает результат возведения основания в степень степени:
Math.pow(2,3) << 8 Math.pow(8,0) << 1 Math.pow(-1,-1) << -1
Math.powв значительной степени был заменен инфиксным оператором возведения в степень ( **), введенным в ES2016, который выполняет точно такую же операцию:
2 ** 3 << 8 8 ** 0 << 1 (-1) ** (-1) << -1
Вычисление корней
Корни — это операция, обратная степеням. Например, поскольку 3 в квадрате равно 9, квадратный корень из 9 равен 3.
Math.sqrtможет использоваться для возврата квадратного корня числа, предоставленного в качестве аргумента:
Math.sqrt(4) << 2 Math.sqrt(100) << 10 Math.sqrt(2) << 1.4142135623730951
Эта функция вернется NaN, если в качестве аргумента будет указано отрицательное число или нечисловое значение:
Math.sqrt(-1) << NaN Math.sqrt("four") << NaN
Но будьте осторожны, потому что JavaScript попытается принудить тип:
Math.sqrt('4') << 2 Math.sqrt(true) << 1
Math.cbrtвозвращает кубический корень числа. Это принимает все числа, включая отрицательные числа. Он также попытается принудить тип, если используется значение, не являющееся числом. Если он не может привести значение к числу, он вернет NaN:
Math.cbrt(1000) << 10 Math.cbrt(-1000) << -10 Math.cbrt("10") << 2.154434690031884 Math.cbrt(false) << 0
Другие корни можно вычислить, используя оператор возведения в степень и дробную степень. Например, корень четвертой степени можно найти, возведя его в четверть степени (или 0,25). Таким образом, следующий код вернет корень четвертой степени из 625:
625 ** 0.25 << 5
Чтобы найти корень пятой степени числа, вы должны возвести его в степень одной пятой (или 0,2):
32 ** 0.2 << 2
В общем, чтобы найти корень n-й степени числа, вы должны возвести его в степень 1/n, поэтому, чтобы найти корень шестой степени из миллиона, вы должны возвести его в степень 1/6:
1000000 ** (1/6) << 9.999999999999998
Обратите внимание, что здесь есть ошибка округления, так как ответ должен быть ровно 10. Это часто происходит с дробными степенями, которые не могут быть точно выражены в двоичном виде.
Также обратите внимание, что вы не можете найти корни отрицательных чисел, если корень четный. Это вернется NaN. Таким образом, вы не можете попытаться найти, например, 10-й корень из −7 (потому что 10 четно):
(-7) ** 0.1 // 0.1 is 1/10 << NaN
Одна из причин, по которой вы можете захотеть вычислить корни, — это вычислить темпы роста. Например, вы хотите увеличить свою прибыль в 10 раз к концу года. Насколько ваша прибыль должна расти каждый месяц? Чтобы выяснить это, вам нужно вычислить 12-й корень из 10 или 10 в двенадцатой степени:
10 ** (1/12) << 1.2115276586285884
Этот результат говорит нам о том, что месячный коэффициент роста должен составлять около 1,21, чтобы к концу года увеличить прибыль в 10 раз. Или, другими словами, вам нужно увеличивать свою прибыль на 21% каждый месяц, чтобы достичь своей цели.
Логарифмы и экспоненты
Логарифмы — или для краткости журналы — можно использовать для нахождения показателя степени вычисления. Например, представьте, что вы хотите решить следующее уравнение:
2ˣ = 100
В приведенном выше уравнении, xконечно, не целое число, потому что 100 не является степенью числа 2. Это можно решить, используя логарифмы по основанию 2:
x = log²(100) = 6.64 (rounded to 2 d.p.)
У Mathобъекта есть log2метод, который будет выполнять этот расчет:
Math.log2(100) << 6.643856189774724
У него также есть log10метод, который выполняет те же вычисления, но использует 10 в качестве базового числа:
Math.log10(100) << 2
Этот результат говорит нам о том, что для получения 100 нужно возвести 10 в степень 2.
Есть еще один метод журнала, который просто Math.log. Это вычисляет натуральный логарифм, который использует число Эйлера ( eпримерно 2,7) в качестве основания. Это значение может показаться странным для использования, но на самом деле оно часто встречается в природе, когда происходит экспоненциальный рост — отсюда и название «натуральные логарифмы»:
Math.log(10) << 4.605170185988092 Math.log(Math.E) << 1
Последнее вычисление показывает, что число Эйлера ( e), хранящееся как константа Math.E, необходимо возвести в степень 1, чтобы получить само себя. Это имеет смысл, потому что любое число в степени 1 на самом деле является самим собой. Те же результаты могут быть получены, если 2 и 10 будут переданы в качестве аргументов для Math.log2и Math.log10:
Math.log2(2) << 1 Math.log10(10) << 1
Зачем тебе логарифмы? При работе с данными, которые растут экспоненциально, обычно используют логарифмическую шкалу, чтобы легче было увидеть скорость роста. Логарифмические шкалы часто использовались для измерения количества ежедневных случаев COVID-19 во время пандемии, поскольку они так быстро росли.
Если вам посчастливилось иметь веб-сайт, популярность которого быстро растет (скажем, удваивается каждый день), вы можете рассмотреть возможность использования логарифмической шкалы, прежде чем отображать график, показывающий, как растет ваша популярность.
Гипотенуза
Возможно, вы помните, как в школе изучали теорему Пифагора. Это говорит о том, что длину наибольшей стороны прямоугольного треугольника ( гипотенузу ) можно найти по следующей формуле:
h² = x² + y²
Здесь x и y — длины двух других сторон.
У Mathобъекта есть hypotметод, который будет вычислять длину гипотенузы при наличии двух других длин в качестве аргументов. Например, если одна сторона имеет длину 3, а другая — 4, мы можем вычислить гипотенузу, используя следующий код:
Math.hypot(3,4) << 5
Но почему это когда-либо может быть полезно? Итак, гипотенуза — это мера кратчайшего расстояния между двумя точками. Это означает, что если вы знаете координаты x и y двух элементов на странице. Вы можете использовать эту функцию, чтобы вычислить, насколько далеко они друг от друга:
const ship = {x: 220, y: 100} const boat = {x: 340, y: 50} const distance = Math.hypot(ship.x - boat.x,ship.y - boat.y)
Я надеюсь, что этот краткий обзор был полезен и помог вам использовать всю мощь объекта JavaScript Math в ваших проектах.
Math
— это встроенный объект, который имеет свойства и методы для математических констант и функций. Это не функциональный объект.
Math
работает с Number
типом. Это не работает с BigInt
.
Description
В отличие от многих других глобальных объектов, Math
не является конструктором. Все свойства и методы Math
являются статическими. Вы ссылаетесь на константу pi как Math.PI
и вызываете функцию синуса как Math.sin(x)
, где x
— аргумент метода. Константы определяются с полной точностью действительных чисел в JavaScript.
Примечание. Многие Math
функции имеют точность, зависящую от реализации.
Это означает,что разные браузеры могут давать разные результаты.Даже один и тот же движок JavaScript на разных ОС или архитектуре может давать разные результаты!
Static properties
Math.E
-
Постоянная Эйлера и основание натуральных логарифмов; приблизительно
2.718
. Math.LN2
-
Натуральный логарифм
2
; приблизительно0.693
. Math.LN10
-
Натуральный логарифм
10
; приблизительно2.303
. Math.LOG2E
-
Логарифм
E
по основанию 2 ; примерно1.443
. Math.LOG10E
-
Логарифм
E
по основанию 10 ; примерно0.434
. Math.PI
-
Отношение длины окружности к ее диаметру; приблизительно
3.14159
. Math.SQRT1_2
-
Квадратный корень из ½; приблизительно
0.707
. Math.SQRT2
-
Корень квадратный из
2
; примерно1.414
.
Static methods
Math.abs()
-
Возвращает абсолютное значение
x
. Math.acos()
-
Возвращает арккосинус
x
. Math.acosh()
-
Возвращает гиперболический арккосинус
x
. Math.asin()
-
Возвращает арксинус
x
. Math.asinh()
-
Возвращает гиперболический арксин числа.
Math.atan()
-
Возвращает арктангенс
x
. Math.atanh()
-
Возвращает гиперболический арктангенс
x
. Math.atan2()
-
Возвращает ароктангенс кванта его аргументов.
Math.cbrt()
-
Возвращает кубический корень из
x
. Math.ceil()
-
Возвращает наименьшее целое число, большее или равное
x
. Math.clz32()
-
Возвращает количество начальных нулевых битов 32-разрядного целого числа
x
. Math.cos()
-
Возвращает косинус
x
. Math.cosh()
-
Возвращает гиперболический косинус
x
. Math.exp()
-
Возвращает e x , где x — аргумент, а e — константа Эйлера (
2.718
…, основание натурального логарифма). Math.expm1()
-
Возвращает вычитание
1
изexp(x)
. Math.floor()
-
Возвращает наибольшее целое число, меньшее или равное
x
. Math.fround()
-
Возвращает ближайшее представление
x
с плавающей запятой одинарной точности . Math.hypot()
-
Возвращает квадратный корень суммы квадратов его аргументов.
Math.imul()
-
Возвращает результат 32-битного целочисленного умножения
x
иy
. Math.log()
-
Возвращает натуральный логарифм (㏒ e ; также ㏑) числа
x
. Math.log1p()
-
Возвращает натуральный логарифм (㏒ e ; также ㏑)
1 + x
для числаx
. Math.log10()
-
Возвращает десятичный логарифм числа
x
. Math.log2()
-
Возвращает логарифм
x
по основанию 2 . Math.max()
-
Возвращает наибольшее из нулей и более чисел.
Math.min()
-
Возвращает наименьшее из нулей и более чисел.
Math.pow()
-
Возвращает основание
x
в степень степениy
(то естьx
y
). Math.random()
-
Возвращает псевдослучайное число от
0
до1
. Math.round()
-
Возвращает значение числа
x
, округленное до ближайшего целого числа. Math.sign()
-
Возвращает знак
x
, указывающий, является лиx
положительным, отрицательным или нулем. Math.sin()
-
Возвращает синус
x
. Math.sinh()
-
Возвращает гиперболический синус
x
. Math.sqrt()
-
Возвращает положительный квадратный корень из
x
. Math.tan()
-
Возвращает тангенс
x
. Math.tanh()
-
Возвращает гиперболический тангенс
x
. Math.trunc()
-
Возвращает целую часть
x
, удаляя все цифры дробной части.
Examples
Преобразование между градусами и радианами
Тригонометрические функции sin()
, cos()
, tan()
, asin()
, acos()
, atan()
и atan2()
ожидают (и возвращают) углы в радианах .
Поскольку люди склонны мыслить в градусах,а некоторые функции (например,CSS-преобразования)могут принимать градусы,неплохо иметь под рукой функции,преобразующие одно в другое:
function degToRad(degrees) { return degrees * (Math.PI / 180); } function radToDeg(rad) { return rad / (Math.PI / 180); }
Вычисление высоты равностороннего треугольника
Если мы хотим вычислить высоту равностороннего треугольника и знаем, что длина его стороны равна 100, мы можем использовать формулу: длина смежного треугольника, умноженная на тангенс угла, равна противоположному.
В JavaScript мы можем сделать это следующим образом:
50 * Math.tan(degToRad(60))
Мы используем нашу degToRad()
для преобразования 60 градусов в радианы, поскольку Math.tan()
ожидает входное значение в радианах.
Возвращение случайного целого числа между двумя границами
Этого можно добиться с помощью комбинации Math.random()
и Math.floor()
:
function random(min, max) { const num = Math.floor(Math.random() * (max - min + 1)) + min; return num; } random(1, 10);
Specifications
Browser compatibility
Desktop | Mobile | Server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Chrome | Edge | Firefox | Internet Explorer | Opera | Safari | WebView Android | Chrome Android | Firefox для Android | Opera Android | Safari на IOS | Samsung Internet | Deno | Node.js | |
E |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
LN10 |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
LN2 |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
LOG10E |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
LOG2E |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
PI |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
SQRT1_2 |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
SQRT2 |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
Math |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
abs |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
acos |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
acosh |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
asin |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
asinh |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
atan |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
atan2 |
1 |
12 |
1 |
4 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
atanh |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
cbrt |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
ceil |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
clz32 |
38 |
12 |
31 |
No |
25 |
7 |
38 |
38 |
31 |
25 |
7 |
3.0 |
1.0 |
0.12.0 |
cos |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
cosh |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
exp |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
expm1 |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
floor |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
fround |
38 |
12 |
26 |
No |
25 |
8 |
38 |
38 |
26 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
hypot |
38 |
12 |
27 |
No |
25 |
8 |
38 |
38 |
27 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
imul |
28 |
12 |
20 |
No |
16 |
7 |
4.4 |
28 |
20 |
15 |
7 |
1.5 |
1.0 |
0.12.0 |
log |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
log10 |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
log1p |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
log2 |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
max |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
min |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
pow |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
random |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
round |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
sign |
38 |
12 |
25 |
No |
25 |
9 |
38 |
38 |
25 |
25 |
9 |
3.0 |
1.0 |
0.12.0 |
sin |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
sinh |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
sqrt |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
tan |
1 |
12 |
1 |
3 |
3 |
1 |
4.4 |
18 |
4 |
10.1 |
1 |
1.0 |
1.0 |
0.10.0 |
tanh |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
trunc |
38 |
12 |
25 |
No |
25 |
8 |
38 |
38 |
25 |
25 |
8 |
3.0 |
1.0 |
0.12.0 |
See also
Number
JavaScript
-
Map.prototype.size
Свойство доступа size возвращает количество элементов в объекте Map.
-
Map.prototype.values()
Метод values()возвращает новый объект-итератор,который содержит для каждого элемента Map порядок вставки.
-
Math.abs()
Функция Math.abs()возвращает абсолютное значение числа.
-
Math.acos()
Функция Math.acos()возвращает арккосинус радиан)числа,то есть x 1 Math.acos arccos единственное y 0 π такое,что forall in [{-1};1],;mathtt{operatorname{Math.acos}(x)}
Как уже было сказано, в javascript определены некоторые стандартные объекты и функции. Сегодня мы рассмотрим объект
math и его методы.
Объект math работает с математическими функциями, а его методы можно использовать для вызова этих функций.
Ниже представлены некоторые методы объекта math:
метод | описание |
---|---|
abs | абсолютное значение |
sin, cos, tan | тригонометрические функции |
log | натуральный логарифм |
exp | экспонента |
pow | показательная функция |
sqrt | квадратный корень |
min | наименьшее значение |
max | наибольшее значение |
Какие-то методы вы будете использовать часто, какие-то редко, а некоторые вам и вовсе не пригодятся.
Рассмотрим пример использования объекта math.
Предположим мы хотим написать сценарий, который будет вычислять площадь треугольника по трем его сторонам.
Для этого нам потребуется использовать формулу Герона:
Для тех, кто давно окончил школу, напоминаю:
S – площадь
a, b, c – длины сторон треугольника
Итак, напишем в html-странице код формы:
math javascript
Сторона 1
Сторона 2
Сторона 3
Результат
Здесь нет ничего нового. Теперь на странице script.js напишем код функции:
function areaOfTriangle(obj){
var a=1*obj.st1.value;
var b=1*obj.st2.value;
var c=1*obj.st3.value;
var p=(a+b+c)/2;
var s=Math.sqrt(p*(p-a)*(p-b)*(p-c));
obj.res.value=s;
}
Здесь мы использовали объект Math и его метод sqrt для извлечения квадратного корня. Выражение, из которого извлекается корень,
должно быть взято в скобки.
Также обратите внимание на первые три строчки функции, они начинаются с 1*, т.е. наши переменные a, b, c мы
умножили на единицу. Зачем? Помните, в первом уроке мы говорили о том, что у каждой переменной есть тип, который определяется
автоматически. Так как наши переменные приходят из текстового поля, то и тип они имеют string, т.е.
они воспринимаются функцией не как числа, а как буквы.
Если бы мы
перемножали эти переменные, то их тип автоматически переопределился бы к number, но мы их складываем, а
знак операции + расценивается в данном случае, как конкатенация строк. Чтобы этого не происходило, мы и
умножили наши переменные на единицу, преобразовав их таким образом к типу number. Ради эксперимента уберите
умножение на единицу в этих трех строках и посмотрите, что “насчитает” сценарий. Затем верните правильный вариант и убедитесь,
что все работает правильно, как в примере ниже.
Вроде все хорошо, но есть один нюанс: если извлекаемый корень является дробным числом, то дробная часть может быть бесконечно
длинной. Для точных вычислений это необходимо, но в большинстве случаев достаточно двух знаков после запятой.
Чтобы округлить результат до N знаков после запятой можно воспользоваться методом toFixed объекта
Number. Синтаксис записи следующий:
(x).toFixed(N)
где x – число, которое надо округлить, а N – число знаков после запятой.
Давайте исправим конец нашей функции и округлим результат до 2 знаков после запятой:
function areaOfTriangle(obj){
var a=1*obj.st1.value;
var b=1*obj.st2.value;
var c=1*obj.st3.value;
var p=(a+b+c)/2;
var s=Math.sqrt(p*(p-a)*(p-b)*(p-c));
s=s.toFixed(2);
obj.res.value=s;
}
Вот теперь все аккуратненько:
Вот на сегодня и все, для тренировки поэкспериментируйте с другими методами объекта math.
Предыдущий урок
Вернуться в раздел
Следующий урок