Устройство вещественных числел в Go

В Go существует 2 типа для выражения чисел с плавающей точкой.

// Диапазон: от -3.4028235e+38 до +3.4028235e+38
// В памяти: 4 байта.
type float32 float32

// Диапазон: от -1.7976931348623157e+308 до +1.7976931348623157e+308
// В памяти: 8 байт.
type float64 float64

Хранение числе с плавающей точкой, а также арифметические операции с ними в Go, реализованы по стандарту IEEE 754(a.k.a IEC 60559).

Основные составляющие числа с плавающей точкой в соответствии со стандартом:

  • Знак числа (S): первый бит показывает положительное число или отрицательное.
  • Экспонента (E): показатель степени, на которую умножается основание (в двоичном представлении это 2). Иными словами, экспонента определяет порядок числа: на сколько число большое или маленькое.
  • Мантисса (M): часть числа, представляющая его значащие цифры. Иными словами точность числа.

(−1)s×M×2E

формула представления числа с плавающей запятой по стандарту IEEE-754
КомпонентБиты float32Биты float64
Знак11
Экспонента811
Мантисса2352
максимальное точно представимое целое число224253

Почему максимальное точно представимое целое для float32 = 2²⁴, хотя мантиса занимает 23 бита?

У float32 мантисса хранится в 23 битах, но реальная точность — 24 бита, так как по стандарту IEEE-754 автоматически добавляется скрытый старший бит = 1.

16 777 216

максимально точно представимое целое 224

Важно понимать, что при превышении этого числа мы столкнемся с неожиданным поведением:

Что выведет данный код и почему?

fmt.Println(float32(16777216) == float32(16777217)) 
  1. Число 16 777 217 требует 25‑го бита, так как не помещается в мантиссу float32;
  2. Оно будет округлено до числа ближайшего к данному, которое float32 может представить;
  3. В нашем случае это будет 16777216

Рассмотрим еще один пример:

Что выведет данный код и почему?

fmt.Println(float32(16_777_216) == float32(16_777_219))
  1. Число 16 777 219 требует 25‑го бита, так как не помещается в мантиссу float32;
  2. Оно будет округлено до числа ближайшего к данному, которое float32 может представить;
  3. В нашем случае это будет 16 777 220

ULP=2E−23

формула округления числа до ближайшего
  • E — реальная степень двойки числа
  • 23 — количество явных бит мантиссы
  • Скрытый бит добавляет 1 к точности, но на ULP влияет только экспонента

Что это обозначает на практике?

  1. Чем больше E, тем больше шаг между числами
  2. Каждый “новый бит” экспоненты удваивает шаг
Пример числаE (степень)ULP = шагКомментарий
16 777 216 ≈ 2²⁴242^(24-23)=2шаг = 2
28 777 218 ≈ 2²⁵252^(25-23)=4шаг = 4
60 000 000 ≈ 226262^(26-23)=8oшаг = 4
2³⁰302^(30-23)=128шаг = 128

Какие проблемы возникают при работе с вещественными числами, а также методы их решения мы рассмотрим в следующих разделах.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *