// Плавающая точка: подводные камни IEEE 754
// Запуск: go run float_demo.go

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println("=== Go: плавающая точка ===")
	fmt.Println()

	// 1. Классика
	fmt.Println("--- 1. Арифметика ---")
	a, b, c := 0.1, 0.2, 0.3
	fmt.Printf("0.1 + 0.2 = %.20f\n", a+b)
	fmt.Printf("0.3       = %.20f\n", c)
	fmt.Printf("0.1 + 0.2 == 0.3? %v\n\n", a+b == c)

	// 2. Потеря точности
	fmt.Println("--- 2. Потеря точности ---")
	big := 1e16
	fmt.Printf("1e16 + 1.0 = %.1f (единица потеряна!)\n", big+1.0)
	fmt.Printf("1e16 + 1.0 == 1e16? %v\n\n", big+1.0 == big)

	// 3. NaN
	fmt.Println("--- 3. NaN ---")
	nan := math.NaN()
	fmt.Printf("NaN == NaN? %v\n", nan == nan)
	fmt.Printf("NaN != NaN? %v\n", nan != nan)
	fmt.Printf("math.IsNaN(): %v\n\n", math.IsNaN(nan))

	// 4. Отрицательный ноль
	fmt.Println("--- 4. Отрицательный ноль ---")
	posZ := 0.0
	negZ := math.Copysign(0, -1)
	fmt.Printf("0.0 == -0.0? %v\n", posZ == negZ)
	fmt.Printf("1.0 / 0.0  = %v\n", 1.0/posZ)
	fmt.Printf("1.0 / -0.0 = %v\n\n", 1.0/negZ)

	// 5. Kahan суммирование
	fmt.Println("--- 5. Наивная vs Kahan суммировка ---")
	n := 1_000_000
	term := 0.1

	naiveSum := 0.0
	for i := 0; i < n; i++ {
		naiveSum += term
	}

	kahanSum := 0.0
	comp := 0.0
	for i := 0; i < n; i++ {
		y := term - comp
		t := kahanSum + y
		comp = (t - kahanSum) - y
		kahanSum = t
	}

	expected := float64(n) * term
	fmt.Printf("Ожидание:       %.20f\n", expected)
	fmt.Printf("Наивная сумма:  %.20f\n", naiveSum)
	fmt.Printf("Kahan сумма:    %.20f\n", kahanSum)
	fmt.Printf("Ошибка наивной: %.2e\n", math.Abs(naiveSum-expected))
	fmt.Printf("Ошибка Kahan:   %.2e\n\n", math.Abs(kahanSum-expected))

	// 6. float32 vs float64
	fmt.Println("--- 6. float32 vs float64 ---")
	var f32 float32 = 16777217 // 2^24 + 1
	var f64 float64 = 16777217
	fmt.Printf("16777217 как float32: %.0f (потеря!)\n", f32)
	fmt.Printf("16777217 как float64: %.0f\n", f64)
}
