Skip to content

Fix number (01-05-02) #703

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

Merged
merged 2 commits into from
Apr 7, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions 1-js/05-data-types/02-number/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let billion = 1_000_000_000;
```js run
let billion = 1e9; // 1 мільярд, буквально: 1 та 9 нулів

alert( 7.3e9 ); // 7.3 мільярдів (таке ж саме що 7300000000 чи 7_300_000_000)
alert( 7.3e9 ); // 7.3 мільярдів (те ж саме, що й 7300000000 чи 7_300_000_000)
```

Іншими словами, `"e"` помножує число на `1` із заданим числом нулів.
Expand Down Expand Up @@ -89,30 +89,30 @@ let b = 0o377; // вісімкова форма 255
alert( a == b ); // true, те саме число 255 з обох сторін
```

Є лише 3 системи числення з такою підтримкою. Для інших систем числення ми повинні використовувати функцію `parseInt` (яку ми побачимо далі в цьому розділі).
Таким способом підтримуються лише 3 системи числення. Для інших систем числення ми повинні використовувати функцію `parseInt` (яку ми побачимо далі в цьому розділі).

## toString(base)

Метод `num.toString(base)` повертає рядкове представлення `num` в системі числення із заданим `base`.
Метод `num.toString(base)` повертає `num` у вигляді рядка, в якому вказане це ж число, але в системі числення із заданим `base`.

Наприклад:
```js run
let num = 255;

alert( num.toString(16) ); // ff
alert( num.toString(2) ); // 11111111
alert( num.toString(16) ); // "ff"
alert( num.toString(2) ); // "11111111"
```

`base` може бути від `2` до `36`. За замовчуванням це `10`.

Загальні випадки використання для цього є:

- **base=16** використовується для шістнадцяткових кольорів, кодування символів тощо, цифри можуть бути `0..9` або `A..F`.
- **base=2** в основному для налагодження бітових операцій, цифри можуть бути `0` або `1`.
- **base=36** є максимальним, цифри можуть бути `0..9` або `A..Z`. Весь латинський алфавіт використовується для позначення числа. Комічно, але користь від системи для найбільших чисел полягає у перетворенні довгого числового ідентифікатора у щось коротше, наприклад, для генерації короткого URL. Для цього достатньо представити його в системі числення з базою `36`:
- **base=2** використовується в основному для налагодження бітових операцій, цифри можуть бути `0` або `1`.
- **base=36** є максимальною, цифри можуть бути `0..9` або `A..Z`. Для позначення такого числа в якості цифер використовується увесь латинський алфавіт. Кумедно, але переведення числа в таку систему числення буває корисним коли ми маємо дуже довгий числовий ідентифікатор і хочемо перетворити його в щось коротше, бо хочемо зробити url коротшим. Для цього таке число можна легко перетворити в тридцяткове число (число в системі числення з базою `36`).:

```js run
alert( 123456..toString(36) ); // 2n9c
alert( 123456..toString(36) ); // "2n9c"
```

```warn header="Дві крапки для виклику методу"
Expand Down Expand Up @@ -181,7 +181,7 @@ alert( num.toString(2) ); // 11111111
alert( num.toFixed(1) ); // "12.4"
```

Зверніть увагу, що результат `toFixed` -- це рядок. Якщо десяткова частина коротша, ніж потрібно, нулі додаються в кінці:
Зверніть увагу, що результат `toFixed` -- це рядок. І в кінці можуть додаватись нулі якщо десяткова частина коротша, ніж потрібно:

```js run
let num = 12.34;
Expand All @@ -192,7 +192,7 @@ alert( num.toString(2) ); // 11111111

## Неточні розрахунки

Із середини, число представлено у 64-бітному форматі [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), тому для його зберігання треба саме 64 біти: 52 з них використовуються для зберігання цифр, 11 -- відповідають за позицію десяткової крапки (для цілих чисел вони дорівнюють нулю), а 1 біт -- для знака.
Із середини, число представлено у 64-бітному форматі [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), тому для його зберігання треба саме 64 біти: 52 з них використовуються для зберігання цифр, 11 -- відповідають за позицію десяткової крапки (для цілих чисел вони дорівнюють нулю), а 1 біт -- для полярності (тобто інформації чи йде перед числом знак мінус, чи ні).

Якщо число занадто велике, та переповнює 64-біти, воно буде перетворене на спеціальне числове значення `Infinity`(Нескінченність):

Expand Down Expand Up @@ -220,7 +220,7 @@ alert( 0.1 + 0.2 ); // 0.30000000000000004

Але чому так відбувається?

Число зберігається в пам’яті у його двійковій формі, послідовність бітів - одиниць і нулів. Але дроби на кшталт `0.1`, `0.2`, які виглядають просто в десятковій системі числення, насправді є нескінченними дробами у своїй двійковій формі.
Число зберігається в пам’яті у його двійковій формі, як послідовність бітів - одиниць і нулів. Але дроби на кшталт `0.1`, `0.2`, які виглядають просто в десятковій системі числення, насправді є нескінченними дробами у своїй двійковій формі.

Іншими словами, що таке `0.1`? Це одиниця розділена на десять `1/10` -- одна десята. У десятковій системі такі числа досить легко представити, але якщо порівняти його з однією третиною: `1/3`, то ми стикаємось з нескінченним дробом `0.33333(3)`.

Expand Down Expand Up @@ -252,7 +252,7 @@ let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // "0.30"
```

Зауважте, що `toFixed` завжди повертає рядок, щоб число гарантовано мало дві цифри після десяткової крапки. Це насправді зручно, якщо у нас є електронні покупки та нам потрібно показати `$0.30`. В інших випадках ми можемо використовувати одинарний плюс, щоб для приведення його до числа:
Зауважте, що `toFixed` завжди повертає рядок, щоб число гарантовано мало дві цифри після десяткової крапки. Та й взагалі це зручно, якщо у нас є електронні покупки та нам потрібно показати `$0.30`. В інших випадках ми можемо використовувати одинарний плюс, якщо хочемо результат `toFixed` конвертувати в число:

```js run
let sum = 0.1 + 0.2;
Expand All @@ -268,7 +268,7 @@ alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001

Отже, підхід множення/ділення зменшує помилку, але не видаляє її повністю.

Іноді можна спробувати уникнути проблем з дробами. Якщо ми маємо справу з магазином, то можемо зберігати ціни в центах замість доларів. Але що робити, якщо ми застосуємо знижку в розмірі 30%? На практиці повністю уникнути дробів вдається досить рідко. Просто округліть їх, щоб вирізати "хвости", коли це потрібно.
Іноді можна спробувати уникнути проблем з дробами. Якщо ми маємо справу з магазином, то можемо зберігати ціни в центах замість доларів. Але що робити, якщо ми застосуємо знижку в розмірі 30%? На практиці повністю уникнути дробів вдається досить рідко. Тому просто округляйте їх, щоб відрізати "хвости", коли це потрібно.

````smart header="Цікавий факт"
Спробуйте запустити:
Expand All @@ -286,7 +286,7 @@ JavaScript не викликає помилку в таких випадках.
```smart header="Два нулі"
Ще одним кумедним наслідком внутрішньої реалізації чисел є наявність двох нулів: `0` і `-0`.

Це тому, що знак представлений одним бітом, тому його можна встановити або не встановити для будь-якого числа, включаючи нуль.
Це тому, що в усіх чисел є один біт для знака. А тому знак можна встановити або не встановити для будь-якого числа, навіть для нуля.

У більшості випадків відмінність непомітна, оскільки оператори підходять до них як до однакових.
```
Expand All @@ -308,13 +308,13 @@ JavaScript не викликає помилку в таких випадках.
alert( isNaN("str") ); // true
```

Але чи потрібна нам ця функція? Чи не можемо ми просто використати порівняння `=== NaN`? Вибачте, але відповідь -- ні. Значення `NaN` унікальне тим, що воно нічому не дорівнює, включаючи себе:
Але чи потрібна нам ця функція? Чи не можемо ми просто використати порівняння `=== NaN`? Вибачте, але відповідь -- ні. Значення `NaN` унікальне тим, що воно нічому не дорівнює, навіть самому собі:

```js run
alert( NaN === NaN ); // false
```

- `isFinite(value)` перетворює свій аргумент в число і повертає `true`, якщо це звичайне число, та `false`, якщо `NaN/Infinity/-Infinity`:
- `isFinite(value)` перетворює свій аргумент в число і повертає `true`, якщо це звичайне число. Або `false`, якщо `NaN/Infinity/-Infinity`:

```js run
alert( isFinite("15") ); // true
Expand All @@ -328,7 +328,7 @@ JavaScript не викликає помилку в таких випадках.
```js run
let num = +prompt("Enter a number", '');

// буде істинним, якщо ви не введете Infinity, -Infinity чи не число
// буде true, якщо ви не введете Infinity, -Infinity чи NaN
alert( isFinite(num) );
```

Expand Down Expand Up @@ -364,12 +364,12 @@ alert( isFinite(num) );
````

```smart header="Порівняння з `Object.is`"
Існує спеціальний вбудований метод `Object.is`, який порівнює значення як `===`, але є більш надійним для двох виключень:
Існує спеціальний вбудований метод `Object.is`, який порівнює значення як `===`, але є більш надійним для двох виняткових випадків:

1. Працює з `NaN`: `Object.is(NaN, NaN) === true`, і це добре.
2. Значення `0` і` -0` різні: `Object.is(0, -0) === false`, технічно це правда, оскільки внутрішньо число має біт знаків, який може бути різним, навіть якщо всі інші біти -- нулі.

У всіх інших випадках `Object.is(a, b)` те саме, що `a === b`.
У всіх інших випадках `Object.is(a, b)` поверне те саме, що й `a === b`.

Ми згадуємо тут `Object.is`, оскільки він часто використовується в специфікації JavaScript. Коли для внутрішнього алгоритму потрібно порівняти два значення, щоб вони були абсолютно однаковими, він використовує `Object.is` (ще його називають [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)).
```
Expand All @@ -389,7 +389,7 @@ alert( +"100px" ); // NaN

Ось для чого призначені `parseInt` та `parseFloat`.

Вони "читають" число з рядка, до поки можуть, у разі помилки зчитане число повертається. Функція `parseInt` повертає ціле число, тоді як `parseFloat` повертає число з плаваючою крапкою:
Вони допоки можуть - доти "читають" число з рядка. І у разі помилки зчитане число повертається. Функція `parseInt` повертає ціле число, тоді як `parseFloat` повертає число з плаваючою крапкою:

```js run
alert( parseInt('100px') ); // 100
Expand Down Expand Up @@ -450,9 +450,9 @@ JavaScript має вбудований [Math](https://developer.mozilla.org/uk/d

## Підсумки

Щоб записати числа з багатьма нулями:
Для написання числа з багатьма нулями:

- Додайте `"e"` з числом нулів до числа. Як і: `123e6` те саме, що `123` з 6 нулями `123000000`.
- Додайте `"e"` з кількістю нулів до числа. Наприклад `123e6` - це те саме, що `123` з 6 нулями `123000000`.
- Від’ємне число після `"е"` призводить до ділення числа на 1 із заданими нулями. Наприклад `123e-6` означає `0.000123` (`123` мільйони).

Для різних систем числення:
Expand All @@ -463,10 +463,10 @@ JavaScript має вбудований [Math](https://developer.mozilla.org/uk/d

Для регулярних тестів чисел:

- `isNaN(value)` перетворює свій аргумент на число, а потім перевіряє його на `NaN`
- `Number.isNaN(value)` перевіряє, чи належить його аргумент до типу `number`, і якщо так, перевіряє його на `NaN`
- `isFinite(value)` перетворює свій аргумент на число, а потім перевіряє, чи не є `NaN/Infinity/-Infinity`
- `Number.isFinite(value)` перевіряє, чи належить його аргумент до типу `number`, і якщо так, перевіряє, чи не є `NaN/Infinity/-Infinity`
- `isNaN(value)` перетворює свій аргумент на число, а потім перевіряє чи він `NaN`
- `Number.isNaN(value)` перевіряє, чи належить його аргумент до типу `number`, і якщо так, перевіряє чи він `NaN`
- `isFinite(value)` перетворює свій аргумент на число, а потім перевіряє, чи не є він `NaN/Infinity/-Infinity`
- `Number.isFinite(value)` перевіряє, чи належить його аргумент до типу `number`, і якщо так, перевіряє, чи не є він `NaN/Infinity/-Infinity`

Для перетворення значень на зразок `12pt` та `100px` у число:

Expand Down