При работе с вещественными числами, или числами с плавающей запятой, могут быть несоответствия при сравнении двух значений, что кажутся одинаковыми. В отличие от целых чисел, IEEE вещественные значения являются «приблизительными». Необходимость конвертации чисел в подходящую для хранения в памяти компьютера форму приводит к небольшим неточностям вследствие округления. К примеру, значение 1.3 может быть представлено в виде 1.29999999999. Сравнение может осуществляться с допустимым отклонением. Для сравнения чисел с произвольной точностью нужен пакет big.
Как сравнить вещественные числа float в Go?
1. Создайте файл tolerance.go
со следующим содержимым:
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
package main import ( "fmt" "math" ) const da = 0.29999999999999998889776975374843459576368331909180 const db = 0.3 func main() { daStr := fmt.Sprintf("%.10f", da) dbStr := fmt.Sprintf("%.10f", db) // Во время форматирования числа в строку // оно округляется до 3. fmt.Printf("Строки %s = %s равны: %v \n", daStr, dbStr, dbStr == daStr) // Числа не равны fmt.Printf("Числа равны: %v \n", db == da) // Точность вещественных значений // ограничена. Для сравнения float // лучше использовать сравнение с допуском. fmt.Printf("Числа равны с учетом ДОПУСКА: %v \n", Equals(da, db)) } const TOLERANCE = 1e-8 // Equals сравнивает вещественные числа // с допуском 1e-8 func Equals(numA, numB float64) bool { delta := math.Abs(numA - numB) if delta < TOLERANCE { return true } return false } |
2. Запустите код в терминале через go run tolerance.go
;
3. Посмотрите на вывод:
1 2 3 |
Строки 0.3000000000 = 0.3000000000 равны: true Числа равны: false Числа равны с учетом ДОПУСКА: true |
4. Создайте файл big.go
со следующим содержимым:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package main import ( "fmt" "math/big" ) var da float64 = 0.299999992 var db float64 = 0.299999991 var prec uint = 32 var prec2 uint = 16 func main() { fmt.Printf("Сравнение float64 с '==' равно: %v\n", da == db) daB := big.NewFloat(da).SetPrec(prec) dbB := big.NewFloat(db).SetPrec(prec) fmt.Printf("A: %v \n", daB) fmt.Printf("B: %v \n", dbB) fmt.Printf("Сравнение big.Float с точностью: %d : %v\n", prec, daB.Cmp(dbB) == 0) daB = big.NewFloat(da).SetPrec(prec2) dbB = big.NewFloat(db).SetPrec(prec2) fmt.Printf("A: %v \n", daB) fmt.Printf("B: %v \n", dbB) fmt.Printf("Сравнение big.Float с точностью: %d : %v\n", prec2, daB.Cmp(dbB) == 0) } |
5. Запустите код в терминале через go run big.go
;
6. Посмотрите на вывод:
1 2 3 4 5 6 7 |
Сравнение float64 с '==' равно: false A: 0.299999992 B: 0.299999991 Сравнение big.Float с точностью: 32 : false A: 0.3 B: 0.3 Сравнение big.Float с точностью: 16 : true |
Функции для сравнивания Float между собой в Golang
Первый способ сравнения вещественных чисел без использования встроенных пакетов (шаги 1-5) требует задействования так называемой константы EPSILON
. Это небольшое значение разницы (дельты) между числами, что позволит считать их равными. Константа может быть со значением около 1е-8
, чего достаточно для определения точности.
Второй вариант не только более сложный, но и более полезный для дальнейшей работы с вещественными числами. Пакет math/big
предлагает тип Float
, что можно настроить для данной точности. Преимущество данного пакета в том, что точность здесь может быть выше, нежели точность типа float64
. По этой причине значения небольшой точности использовались для примера округления и сравнения данной точности.
Обратите внимание, что числа da
и db
равны при использовании 16-битной точности, но не равны при использовании 32-битной точности. Максимальную настраиваемую точность можно получить из константы big.MaxPrec
.
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»