Этот код может не работать по нескольким причинам. Тестовые значения, которые вы опубликовали в комментариях (12341265425612411512
и 123412654256124115122111
), усекаются, потому что они слишком велики . Они будут усечены до количества значащих цифр, которые система может представлять внутри, что является частью спецификации языка. В базе 10 эти числа равны 188,072,944,321,827,658
и 770,346,779,942,206,088,265
, что на несколько порядков больше, чем наибольшее целое число Javascript. Более подробные сведения об ограничениях размера для числовых значений в Javascript описаны ниже.
Как обнаружить такие вещи - более сложная проблема. Вы сказали, что вы новичок в программировании, поэтому вот несколько методических советов, которые помогут вам оставаться на пути, где такие ошибки легче найти и исправить.
В общем, вы хотите написать код, чтобы эти проблемы стали очевидными, а также чтобы можно было выделить такие небольшие кусочки кода для тестирования и повторного использования. Как правило, рекомендуется писать код, который можно использовать повторно без перезаписи.
Примечания к вашему коду
Вопросы проверки и разделения входных данных
Каждый раз, когда вы принимаете входные данные, вы хотите подумать о том, как их следует проверять и очищать. У вас может быть плохой ввод. Если вы ожидаете ввода восьмеричного числа, что произойдет, если программа обнаружит десятичное число или что-то еще? Среди прочего, вы можете превысить максимальное числовое значение, которое может быть представлено в системе, как вы нашли.
Максимальное безопасное целое число
В Javascript ограничение размера для числа, анализируемого как целое число, называется Number.MAX_SAFE_INTEGER
.
Если произвольно большие числа пользователя превышают то, что Javascript может представлять как целое число, сохраненное значение потеряет точность. Числовые объекты внутренне представлены в виде двойных чисел, поэтому в реализации существует ограничение по размеру. Ваше восьмеричное входное значение читается с помощью parseInt, а используемые вами тестовые значения превышают верхнюю границу для чисел, представляющих целые числа. Это является частью спецификации языка, поэтому речь идет не только о написании более способной Javascript реализации.
С http://www.ecma-international.org/ecma-262/5.1/#sec-8.5
The Number type has exactly 18437736874454810627 (that is, 264−253+3) values, representing the double-precision 64-bit format IEEE 754 values as specified in the IEEE Standard for Binary Floating-Point Arithmetic, except that the 9007199254740990 (that is, 253−2) distinct “Not-a-Number” values of the IEEE Standard are represented in ECMAScript as a single special NaN value.
Вы можете проверить свое значение по отношению к Number.MAX_SAFE_INTEGER
, чтобы убедиться, что ваш номер в границах.
if (your_number_value > Number.MAX_SAFE_INTEGER) {
// ... the value is too large to be represented as an integer in Javascript
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
Вы также можете проверить значения с помощью Number.isSafeInteger()
, что может быть предпочтительным Я показываю оба, для полноты.
if (Number.isSafeInteger(your_number_value) === false) {
// ... the value is too large to be represented as an integer in Javascript
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger
BigInt - произвольно большие числа
Если вы хотите иметь возможность обрабатывать произвольно большие числовые значения, вам нужно использовать тип BigInt Javascript вместо Number. BigInt - это тип, разработанный для решения этой проблемы с использованием числовых значений c, которые превышают то, что может обработать Number. Большинство систем не имеют практической необходимости в сколь угодно большом числе, но тип BigInt должен присутствовать для приложений, которым он нужен.
BigInt еще не полностью доступен. Javascript имеет много реализаций, и изменения происходят в процессе стандартизации. Следующая ссылка относится к последней версии спецификации, в которой был указан BigInt. Его реализация находится в стадии разработки, но в настоящее время она доступна не везде (февраль 2020 г.).
https://tc39.es/ecma262/#sec -bigint-constructor
Из-за этих ограничений вы Мы получили решение, состоящее из двух частей.
Во-первых, вы хотите обнаружить проблему, чтобы вы могли потенциально выполнить переход в своем коде, если только по какой-либо другой причине, кроме как показать ошибку.
Во-вторых, вы можете использовать это обнаружение для решения проблемы большого числа. Вы можете теоретически разобрать и представить число самостоятельно.
Math. js library
Реализация произвольно большого типа чисел - это совершенно новая проблема, и это не то, где вы хотите провести время, если вы новичок. Для этого есть удобная библиотека Javascript, которую вы можете научиться использовать. Вот ссылка на раздел BigNumbers документации по математике. js.
https://mathjs.org/docs/datatypes/bignumbers.html#bignumbers
Вот ссылка на страницу, где находится математика. js человек описывают концептуальное подтверждение реализации BigInt.
https://mathjs.org/examples/advanced/use_bigint.js.html
Это может быть больше, чем вам нужно. Обратите внимание, что изменения ниже приведут к ошибке для этих очень больших чисел, и для превышения этих пределов требуется очень большое число. Вероятно, разочаровывает мысль о том, что существуют ограничения на число, но на самом деле вы не найдете калькулятор в естественном мире, который не имеет своих ограничений. У лучших калькуляторов есть более щедрые границы, но цифры в резюме похожи на зефир. Они просто продолжают расти, и мы ничего не можем сделать, чтобы остановить их. В реальном мире мы можем исчерпать себя. Но опять же, даже пределы для безопасных целых чисел являются относительно огромными значениями.
Javascript Максимальное числовое значение
Еще один предел, который необходимо знать, помимо "безопасного" целое число "предел, составляет Javascript Number.MAX_VALUE
. Это наибольшее число, которое может хранить c значение Javascript, независимо от того, какое это число. По сути, система не может управлять значениями, требующими более 128 байт, что является относительно большим числом для практических целей. Если вы пишете код, который может принимать произвольный ввод цифр c, и, возможно, если вы возводите очень большие числа в высокие показатели, значения могут теоретически превышать максимальное значение чисел c, которое может быть представлено в Javascript. Маловероятно, что у пользователя будет такое большое значение для любой реальной цели, которая имеет значение для всех значащих цифр, не говоря уже о ручном вводе такого большого числа в ваш калькулятор по любой реальной и полезной причине. Но если числа в этом порядке используются в приложении Javascript, они начнут терять точность, превышающую значение Number.MAX_VALUE
. Вы можете проверить этот случай и решить, что делать.
Обратите внимание, что верхняя граница Number.MAX_SAFE_INTEGER
(2 ^ 53-1) намного меньше, чем Number.MAX_VALUE
(2 ^ 1024), так что если вы При тестировании на Number.MAX_SAFE_INTEGER
в качестве верхней границы вам не нужно беспокоиться о пределе Number.MAX_VALUE
. Просто знайте, что это там.
Number.MAX_VALUE
на MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_VALUE
if (your_number_value > Number.MAX_VALUE) {
// ... the number is too large for the Javascript implementation.
}
Прослушиватели событий
Вы захотите пересмотреть способ настройки прослушивателей событий. То, как вы структурировали код, не является абсолютно неправильным; Значение «num» всегда будет правильным, но оно неоправданно неэффективно. Вы запускаете внешний прослушиватель событий каждый раз, когда происходит событие «input», и внутренняя функция addEventListener вызывается каждый раз. Вы хотите сделать это только один раз.
Вы можете прочитать полную информацию о событии ввода здесь: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event
Тип ввода
Возможно, вы захотите использовать тип ввода по умолчанию («текст»), а не тип «число». Я понимаю, что это может показаться нелогичным, потому что инструмент предназначен для чисел, но вы действительно используете это поле в качестве поля ввода текста. Поля ввода типа «число» имеют тенденцию вызывать цифровую клавиатуру на платформах с программными клавиатурами, но у числовых полей есть некоторые атрибуты, которые вы не используете («min», «max» и «step»).
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number
Поиск и устранение этой проблемы
Сначала рассмотрим выделение некоторых функций, чтобы мы могли проверить атомы c единицы кода, например функция, отвечающая за преобразование чисел.
/**
* convert_octal_string
*
* A function to convert strings representing Octals to other place values
*
* Returns an Array of String values
* Position 0 - null if there are no errors, or an error string if something went wrong
* Position 1 - binary string value
* Position 2 - octal string value
* Position 3 - decimal string value
* Position 4 - hexadecimal string value
*/
function convert_octal_string (octal_string) {
if (!octal_string || typeof octal_string !== "string") {
return ["Error: function convert_octal_string expects a string parameter", octal_string, typeof octal_string];
}
var octal_string_len = octal_string.length;
if (octal_string_len == 0) {
return ["Error: function convert_octal_string received an empty string.", octal_string, typeof octal_string];
}
/* Only accept valid input characters (octal values) */
var allowed_characters = ["0", "1", "2", "3", "4", "5", "6", "7"];
for (var i=0; i<octal_string_len; i+=1) {
var each_char = octal_string[i];
if (!allowed_characters.includes(each_char)) {
return ["Error: convert_octal_string received an input string with non-octal values.", octal_string, typeof octal_string];
}
}
/* Parse the number as an octal */
var result = parseInt(octal_string, 8);
/* Validation: Make sure we have a good result. */
if (isNaN(result)) {
return ["Unexpected Error. The given value could not be understood."];
}
/* Validation: Make sure we haven't exceeded the maximum possible value. */
if (result > Number.MAX_SAFE_INTEGER) {
return ["Error: Large Number. The given number is too large."];
}
/* Convert to strings */
var str_octal = result.toString(8);
var str_decimal = result.toString(10);
var str_binary = result.toString(2);
var str_hexadecimal = result.toString(16).toUpperCase();
return [null, str_binary, str_octal, str_decimal, str_hexadecimal];
}
Тесты
Напишите несколько тестов, которые включают в себя хороший ввод / вывод и вход, который терпит неудачу. Вышеприведенная функция написана таким образом, что дает согласованный набор выходных данных, чтобы вызывающая сторона всегда могла понять, что произошло. Следующие тесты написаны как простая структура, которая содержит входные значения и результат, который мы ожидаем от функции, если все работает правильно.
/**
* Tests for Octal number conversion
*/
/* 1. Some simple test data */
var octal_tests = [
{
"name": "Basic Sanity Check 1",
"octal_string": "0",
"expected_results": [null, "0", "0", "0", "0"]
},
{
"name": "Basic Sanity Check 2",
"octal_string": "1",
"expected_results": [null, "1", "1", "1", "1"]
},
{
"name": "Basic Sanity Check 3",
"octal_string": "10",
"expected_results": [ null, "1000", "10", "8", "8"]
},
{
"name": "Place value boundary 1",
"octal_string": "11",
"expected_results": [null, "1001", "11", "9", "9"]
},
{
"name": "Place value boundary 2",
"octal_string": "17",
"expected_results": [null, "1111", "17", "15", "F"]
},
{
"name": "Large number test using Number implementation",
"octal_string": "123412654256124115122111",
"expected_results": [
"Error: Large Number. The given number is too large."
]
},
{
"name": "Large number test using BigInt implementation",
"octal_string": "123412654256124115122111",
"expected_results": [
null,
"10100111000010101101011000101011100010101000010011010010100100",
"123412654256124115122111",
"770346779942206088265",
"29C2B58AE2A134A449"
]
}
];
Выполните ваши тесты
/* 2. A simple way to run the tests. */
window.addEventListener("load", function () {
/* 1. Check the tests */
octal_tests.map(function (t) {
var test_name = t.name;
var test_input = t.octal_string;
var results = convert_octal_string(test_input);
var expectations = t.expected_results;
if (results.length !== expectations.length) {
console.error(test_name + ": Results do not match expectations for test", results, expectations);
return;
} else {
for (var i=0, len = results.length; i<len; i += 1) {
var each_result = results[i]
each_expected = expectations[i];
if (each_result !== each_expected) {
console.warn(test_name + ": [FAIL] - result != expected value " + each_result + " === " + each_expected);
} else {
console.log(test_name + ": [PASS] - checked " + each_result + " === " + each_expected);
}
}
}
});
}, false);
Наконец, подключите интерфейс.
/**
* Octal number conversion UI
*/
window.addEventListener("load", function () {
/* 1. Get the elements you need */
var form = document.getElementById("form"),
input = document.getElementById("octal-input-field"),
button_calc = document.getElementById("calc-button"),
output_decimal = document.getElementById("output-decimal"),
output_binary = document.getElementById("output-binary"),
output_hexadecimal = document.getElementById("output-hexadecimal");
/* 2. Add a single click event handler. */
button_calc.addEventListener("click", function (e) {
e.preventDefault();
/* 2a. Get the inputted string, parse it into an octal Number value */
var num_str = input.value;
var result = convert_octal_string(num_str);
/* 2b. Update the output fields with each result. */
if (result[0] !== null) {
output_decimal.innerHTML = "";
output_binary.innerHTML = "";
output_hexadecimal.innerHTML = "";
alert(result[0]);
return;
} else {
output_binary.innerHTML = result[1];
output_decimal.innerHTML = result[3];
output_hexadecimal.innerHTML = result[4];
}
return false;
});
/* 3. prevent the form from submitting, since it's not really being used. */
form.addEventListener("submit", function (e) {
e.preventDefault();
return false;
}, false);
}, false);
Полный код
Ниже приведена полная версия страницы со всеми изменениями, которые я описал выше. Я надеюсь, что вы найдете это полезным.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Octal Converter</title>
<!-- Latest compiled and minified CSS -->
<!--<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="css/bootstrap.min.css"> -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<style>
body{
margin-top: 40px;
background: #333;
color: #fff;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.output_field {
word-wrap: break-word;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<h1 class="display-4 text-center mb-3"> Octal Converter</h1>
<div class="form-group">
<form id="form">
<input id="octal-input-field" type="text" class="form-control form-control-lg mb-3" placeholder="Enter the Octal Value...." />
<div>
<input id="calc-button" value="Convert" type="button" name="Go" color="black" class="btn button-danger offset-md-5 mb-1"/>
</div>
</form>
<div id="output">
<div class="card card-primary mb-2">
<div class="card-block">
<h4>Decimal: </h4>
<div class="output_field" id="output-decimal"></div>
</div>
</div>
<div class="card card-success mb-2">
<div class="card-block">
<h4>Binary: </h4>
<div class="output_field" id="output-binary"></div>
</div>
</div>
<div class="card card-danger mb-2">
<div class="card-block">
<h4>Hexadecimal: </h4>
<div class="output_field" id="output-hexadecimal"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
/**
* convert_octal_string
*
* A function to convert strings representing Octals to other place values
*
* Returns an Array of String values
* Position 0 - null if there are no errors, or an error string if something went wrong
* Position 1 - binary string value
* Position 2 - octal string value
* Position 3 - decimal string value
* Position 4 - hexadecimal string value
*/
function convert_octal_string (octal_string) {
if (!octal_string || typeof octal_string !== "string") {
return ["Error: function convert_octal_string expects a string parameter", octal_string, typeof octal_string];
}
var octal_string_len = octal_string.length;
if (octal_string_len == 0) {
return ["Error: function convert_octal_string received an empty string.", octal_string, typeof octal_string];
}
/* Only accept valid input characters (octal values) */
var allowed_characters = ["0", "1", "2", "3", "4", "5", "6", "7"];
for (var i=0; i<octal_string_len; i+=1) {
var each_char = octal_string[i];
if (!allowed_characters.includes(each_char)) {
return ["Error: convert_octal_string received an input string with non-octal values.", octal_string, typeof octal_string];
}
}
/* Parse the number as an octal */
var result = parseInt(octal_string, 8);
/* Validation: Make sure we have a good result. */
if (isNaN(result)) {
return ["Unexpected Error. The given value could not be understood."];
}
/* Validation: Make sure we haven't exceeded the maximum possible value. */
if (result > Number.MAX_SAFE_INTEGER) {
return ["Error: Large Number. The given number is too large."];
}
/* Convert to strings */
var str_octal = result.toString(8);
var str_decimal = result.toString(10);
var str_binary = result.toString(2);
var str_hexadecimal = result.toString(16).toUpperCase();
return [null, str_binary, str_octal, str_decimal, str_hexadecimal];
}
/**
* Tests for Octal number conversion
*/
/* 1. Some simple test data */
var octal_tests = [
{
"name": "Basic Sanity Check 1",
"octal_string": "0",
"expected_results": [null, "0", "0", "0", "0"]
},
{
"name": "Basic Sanity Check 2",
"octal_string": "1",
"expected_results": [null, "1", "1", "1", "1"]
},
{
"name": "Basic Sanity Check 3",
"octal_string": "10",
"expected_results": [ null, "1000", "10", "8", "8"]
},
{
"name": "Place value boundary 1",
"octal_string": "11",
"expected_results": [null, "1001", "11", "9", "9"]
},
{
"name": "Place value boundary 2",
"octal_string": "17",
"expected_results": [null, "1111", "17", "15", "F"]
},
{
"name": "Large number test using Number implementation",
"octal_string": "123412654256124115122111",
"expected_results": [
"Error: Large Number. The given number is too large."
]
},
{
"name": "Large number test using BigInt implementation",
"octal_string": "123412654256124115122111",
"expected_results": [
null,
"10100111000010101101011000101011100010101000010011010010100100",
"123412654256124115122111",
"770346779942206088265",
"29C2B58AE2A134A449"
]
}
];
/* 2. A simple way to run the tests. */
window.addEventListener("load", function () {
/* 1. Check the tests */
octal_tests.map(function (t) {
var test_name = t.name;
var test_input = t.octal_string;
var results = convert_octal_string(test_input);
var expectations = t.expected_results;
if (results.length !== expectations.length) {
console.error(test_name + ": Results do not match expectations for test", results, expectations);
return;
} else {
for (var i=0, len = results.length; i<len; i += 1) {
var each_result = results[i]
each_expected = expectations[i];
if (each_result !== each_expected) {
console.warn(test_name + ": [FAIL] - result != expected value " + each_result + " === " + each_expected);
} else {
console.log(test_name + ": [PASS] - checked " + each_result + " === " + each_expected);
}
}
}
});
}, false);
/**
* Octal number conversion UI
*/
window.addEventListener("load", function () {
/* 1. Get the elements you need */
var form = document.getElementById("form"),
input = document.getElementById("octal-input-field"),
button_calc = document.getElementById("calc-button"),
output_decimal = document.getElementById("output-decimal"),
output_binary = document.getElementById("output-binary"),
output_hexadecimal = document.getElementById("output-hexadecimal");
/* 2. Add a single click event handler. */
button_calc.addEventListener("click", function (e) {
e.preventDefault();
/* 2a. Get the inputted string, parse it into an octal Number value */
var num_str = input.value;
var result = convert_octal_string(num_str);
/* 2b. Update the output fields with each result. */
if (result[0] !== null) {
output_decimal.innerHTML = "";
output_binary.innerHTML = "";
output_hexadecimal.innerHTML = "";
alert(result[0]);
return;
} else {
output_binary.innerHTML = result[1];
output_decimal.innerHTML = result[3];
output_hexadecimal.innerHTML = result[4];
}
return false;
});
/* 3. prevent the form from submitting, since it's not really being used. */
form.addEventListener("submit", function (e) {
e.preventDefault();
return false;
}, false);
}, false);
</script>
</body>
</html>