операций с плавающей точкой в
Вот пример кода в go:
package main
import "fmt"
func mult32(a, b float32) float32 { return a*b }
func mult64(a, b float64) float64 { return a*b }
func main() {
fmt.Println(3*4.3) // A1, 12.9
fmt.Println(mult32(3, 4.3)) // B1, 12.900001
fmt.Println(mult64(3, 4.3)) // C1, 12.899999999999999
fmt.Println(12.9 - 3*4.3) // A2, 1.8033161362862765e-130
fmt.Println(12.9 - mult32(3, 4.3)) // B2, -9.536743e-07
fmt.Println(12.9 - mult64(3, 4.3)) // C2, 1.7763568394002505e-15
fmt.Println(12.9 - 3*4.3) // A4, 1.8033161362862765e-130
fmt.Println(float32(12.9) - float32(3)*float32(4.3)) // B4, -9.536743e-07
fmt.Println(float64(12.9) - float64(3)*float64(4.3)) // C4, 1.7763568394002505e-15
}
Результаты различия между линиями A1, B1 и C1 понятны. Однако, начиная с А2 до С2, приходит магия. В результате ни Б2, ни С2 соответствует результату от линии А2. То же самое верно и для линий x2 (x = A, B или C) - но выходы x2 и x4 одинаковы.
Просто для уверенности давайте напечатаем результаты в двоичном виде.
fmt.Printf("%bn", 3*4.3) // A11, 7262054399134925p-49
fmt.Printf("%bn", mult32(3, 4.3)) // B11, 13526631p-20
fmt.Printf("%bn", mult64(3, 4.3)) // C11, 7262054399134924p-49
fmt.Printf("%bn", 12.9 - 3*4.3) // A12, 4503599627370496p-483
fmt.Printf("%bn", 12.9 - mult32(3, 4.3)) // B12, -8388608p-43
fmt.Printf("%bn", 12.9 - mult64(3, 4.3)) // C12, 4503599627370496p-101
fmt.Printf("%bn", 12.9 - 3*4.3) // A14, 4503599627370496p-483
fmt.Printf("%bn", float32(12.9) - float32(3)*float32(4.3)) // B14, -8388608p-43
fmt.Printf("%bn", float64(12.9) - float64(3)*float64(4.3)) // C14, 4503599627370496p-101
Некоторые факты из приведенного выше кода (один в форме бин):
- есть разница между строками А11 и С11 (последняя цифра-непосредственно перед показателем степени).
- линии A12 и C12 почти одинаковы (за исключением экспоненты!!!), то же самое можно наблюдать между линиями А14 и С14.
И тут возникают вопросы:
- как выполняются вычисления голых (naked :)) чисел? (вычисления в каждой строке Axx)
- выполняются ли они компилятором / чем угодно?
- если да, то почему они отличаются? Оптимизация?
- являются ли они вычисляется в некоторой системе, которая отличается от IEE-754?
- если да, то почему? Оправдывает ли такой подход достижение более точной точности?
Код был протестирован на 64-битном linux под обоими "go run" и "go build" (go1.0.3), а также на этом сайте: http://tour.golang.org/
2 ответа:
Числовые константы представляют собой значения произвольной точности и не переполняются.
- представляют собой целочисленные константы, имеющие не менее 256 бит.
- представляют константы с плавающей запятой, включая части комплексной константы, с мантиссой не менее 256 бит и знаковым показателем степени не менее 32 бит.
Да, компилятором для констант времени компиляции.
Да, это так. другое: требуется большая точность. См. 1.
Да, см.
Минимизировать накопление ошибок с плавающей запятой для многочленных выражений констант с плавающей запятой.
Конечно, да. Может ли достижение более низкой точности быть когда-либо целью? Достаточно того, что операции с плавающей запятой во время выполнения внутренне несовершенны, нет необходимости добавлять больше неточностей от постоянных выражений.
Представляют константы с плавающей запятой, включая части комплексной константы, с мантиссой не менее 256 бит и знаковым показателем степени не менее 32 бит.
Обратите внимание, что Go 1.8 (в настоящее время бета-версия в 4 квартале 2016 года, выпущена в 1 квартале 2017 года) изменяет это определение:
Спецификация языка теперь требует только, чтобы реализации поддерживали до 16-битных экспонент в константах с плавающей запятой.
Это не влияет ни на "gc
" или компиляторыgccgo
, оба из которых все еще поддерживают 32-разрядные экспоненты.Это происходит от изменения 17711
16-битный двоичный показатель степени допускает постоянный диапазон, охватывающий примерно диапазон от 7e-9865 до 7e9863, что более чем достаточно для любой практической и гипотетической постоянной арифметики.
spec
: требуется 16-битный минимальный показатель степени в константах, а не 32Кроме того, до недавнего времени
Наконец, ограничение минимального поддерживаемого диапазона значительно снижает сложность реализации в области, которая вряд ли имеет значение в реальности для новых или альтернативных реализаций, совместимых со спецификациями, которые не полагаются или не могут полагаться на ранее существовавшие арифметические пакеты точности arbitratry, поддерживающие 32-битный диапазон экспонент. Это технически изменение языка, но по причинам, упомянутым выше, это вряд ли повлияет на какие-либо реальные программы, и, конечно, не программы, скомпилированные с компиляторами gc или gccgo, поскольку они в настоящее время поддерживают до 32-битных экспонент.cmd/compile
не мог справиться во всяком случае, очень большие экспоненты корректны; то есть вероятность того, что любые реальные программы (кроме тестов, которые исследуют угловые случаи) будут затронуты, близка к нулю.См. выпуск 13572 упоминая, что:
В Go 1.4 компилятор отклонил экспоненты больше 10000 (из-за знания, что код не работает для больших экспонент) без каких-либо жалоб от пользователей.
В более ранние версии Go, большие экспоненты были молчаливо неправильно обработаны, опять же без каких-либо жалоб от пользователей.