Модуль
1Hello, World!2Переменные в Go3Целые числа4Строки и руны5float64 и числа с плавающей точкой← вы здесь6if, switch, for7bool и логические операторы8Константы и iota9Пакет fmt: форматирование вывода
Урок 5~15 минут

float64 и числа с плавающей точкой

float32 и float64

В Go два типа для дробных чисел:

go
var f32 float32 = 3.14   // 32 бита, ~7 значимых цифр
var f64 float64 = 3.14   // 64 бита, ~15 значимых цифр

Используй float64 по умолчанию. float32 нужен только когда критична память (графика, машинное обучение) или при работе с C-библиотеками. В остальных случаях потеря точности float32 создаст больше проблем, чем выгода от экономии 4 байт.

Сокращённая запись (тип выводится как float64):

go
x := 3.14       // float64
y := 3.14e10    // float64 = 31400000000
z := .5         // float64 = 0.5

IEEE 754: как хранится число

Каждый float64 — это 64 бита, разбитых на три поля:

[знак: 1 бит][экспонента: 11 бит][мантисса: 52 бита]

Значение вычисляется по формуле: (-1)^знак × 1.мантисса × 2^(экспонента−1023)

Интерактивный блок выше показывает битовое разложение для любого числа. Введи 0.1 и посмотри — ни одна из 52 бит не даёт точного представления. Число 0.1 в двоичной системе — бесконечная периодическая дробь, как 1/3 в десятичной.

Специальные значения

ЗначениеБитыПолучается из
+Infexp=11111111111, mantissa=01.0/0.0
-Infsign=1, exp=11111111111, mantissa=0-1.0/0.0
NaNexp=11111111111, mantissa≠00.0/0.0, math.Sqrt(-1)
0все биты = 0по умолчанию
go
import "math"
 
x := 1.0 / 0.0      // +Inf
y := -1.0 / 0.0     // -Inf
z := 0.0 / 0.0      // NaN
w := math.Sqrt(-1)  // NaN
 
math.IsInf(x, 1)    // true  (положительная бесконечность)
math.IsInf(y, -1)   // true  (отрицательная)
math.IsNaN(z)       // true
 
// NaN не равно самому себе — единственный такой тип!
z == z              // false

Проблема точности: 0.1 + 0.2 ≠ 0.3

Это не баг Go. Это фундаментальное ограничение IEEE 754:

go
a := 0.1 + 0.2
fmt.Println(a)        // 0.30000000000000004
fmt.Println(a == 0.3) // false

Число 0.1 в двоичной: 0.0001100110011001100... (бесконечная дробь). При записи в 52 бита происходит округление. Сложение двух таких округлённых чисел накапливает ошибку.

0.1 + 0.2 в Go
Ctrl+Enter
1

Как сравнивать float правильно

go
import "math"
 
a, b := 0.1+0.2, 0.3
 
// НЕПРАВИЛЬНО:
a == b  // false
 
// ПРАВИЛЬНО: сравнение с epsilon
const epsilon = 1e-9
math.Abs(a-b) < epsilon  // true
 
// Для денег и финансов — используй целые числа (копейки):
price := 299   // 2.99 рублей в копейках
tax   := 27    // 0.27 рублей
total := price + tax  // 326 копеек = 3.26 рубля

Правило: если деньги — никогда float. Используй int (центы/копейки) или пакет github.com/shopspring/decimal.


Конвертация типов

go
var i int     = 42
var f float64 = float64(i)   // 42.0
var back int  = int(f)        // 42
 
// int() всегда усекает к нулю (не округляет!):
int(3.9)   // 3
int(-3.9)  // -3
int(3.1)   // 3
 
// Правильное округление:
math.Round(3.5)   // 4.0
math.Floor(3.9)   // 3.0 (вниз)
math.Ceil(3.1)    // 4.0 (вверх)
math.Trunc(3.9)   // 3.0 (к нулю)
 
// int после округления:
result := int(math.Round(3.9))  // 4

Осторожно с большими числами:

go
var big float64 = 1e18
small := int(big)  // OK на 64-битных платформах, int64 = 9.2e18
 
var tooBig float64 = 1e20
overflow := int(tooBig)  // undefined behavior!

Пакет math

Все стандартные математические функции принимают и возвращают float64:

go
import "math"
 
// Тригонометрия (аргументы в радианах)
math.Sin(math.Pi/2)  // 1.0
math.Cos(0)          // 1.0
math.Tan(math.Pi/4)  // 1.0 (приблизительно)
 
// Степени и логарифмы
math.Sqrt(16)        // 4.0
math.Pow(2, 10)      // 1024.0
math.Log(math.E)     // 1.0
math.Log2(1024)      // 10.0
math.Log10(100)      // 2.0
 
// Округление
math.Round(2.5)  // 3.0  (банковское: к ближайшему чётному)
math.Round(3.5)  // 4.0
 
// Абсолютное значение
math.Abs(-3.14)  // 3.14
 
// Min/Max (Go 1.21+ также есть встроенные min/max для чисел)
math.Min(3.0, 5.0)  // 3.0
math.Max(3.0, 5.0)  // 5.0

Константы

go
math.Pi    // 3.141592653589793
math.E     // 2.718281828459045
math.Phi   // 1.618033988749895 (золотое сечение)
math.Sqrt2 // 1.4142135623730951
 
math.MaxFloat64              // 1.7976931348623157e+308
math.SmallestNonzeroFloat64  // 5e-324

fmt и вывод float

go
f := 3.14159265358979
 
fmt.Println(f)           // 3.14159265358979
fmt.Printf("%.2f\n", f) // 3.14  (2 знака после запятой)
fmt.Printf("%.0f\n", f) // 3     (без дробной части)
fmt.Printf("%e\n", f)   // 3.141593e+00  (научная нотация)
fmt.Printf("%g\n", f)   // 3.14159265358979  (компактный)
fmt.Printf("%9.3f\n", f)// ____3.142  (ширина поля 9, 3 знака)

Для конвертации в строку:

go
import "strconv"
 
s := strconv.FormatFloat(3.14159, 'f', 2, 64)   // "3.14"
s2 := strconv.FormatFloat(3.14159, 'g', -1, 64) // "3.14159" (минимум цифр)
 
f, err := strconv.ParseFloat("3.14", 64)  // 3.14, nil

Когда float64 недостаточен

Если нужна абсолютная точность (финансы, криптография, научные вычисления):

go
import "math/big"
 
// big.Float — произвольная точность
a := new(big.Float).SetString("0.1")
b := new(big.Float).SetString("0.2")
sum := new(big.Float).Add(a, b)
fmt.Println(sum.Text('f', 20))
// 0.30000000000000000000  ← точно!
 
// big.Rat — точные рациональные дроби
r1 := big.NewRat(1, 3)  // 1/3
r2 := big.NewRat(2, 3)  // 2/3
r3 := new(big.Rat).Add(r1, r2)
fmt.Println(r3)  // 1/1

math/big медленнее обычного float64 примерно в 10-100 раз — используй только там, где точность критична.


В следующем уроке разберём управляющие конструкции — if, switch, for и новшества Go 1.22.

Никогда не сравнивай float через ==. Используй math.Abs(a-b) < epsilon. Это правило действует во всех языках, не только в Go.
IEEE 754 — биты float64
ЗНАК
0
1 бит
ЭКСПОНЕНТА (смещённая на 1023)
0
1
1
1
1
1
1
1
0
1
1
11 бит → 2^-4 = 6.25e-2
МАНТИССА (дробная часть)
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
1
0
52 бита
Знак
+
Экспонента
-4
Значение
0.10000000000000001
Hex
0x3fb999999999999a
Проблема точности
0.1 + 0.20.30000000000000004ожидалось: 0.3
0.1 + 0.2 == 0.3falseожидалось: true
1.0 + 2.03
0.1 * 30.30000000000000004ожидалось: 0.3
math.Abs(0.1+0.2-0.3) < 1e-9true
Паттерны работы с float
x := 1.0 / 0.0     // +Inf
y := -1.0 / 0.0    // -Inf
z := 0.0 / 0.0     // NaN
math.IsInf(x, 1)   // true
math.IsNaN(z)      // true
💡 Float64 не паникует при делении на 0 — возвращает Inf или NaN
🎯
Миссия 1 из 5
Чему равно 0.1 + 0.2 в Go (точный результат)?