Точность с плавающей точкой на разных языках - PullRequest
5 голосов
/ 16 октября 2019

В настоящее время я делаю расчеты расстояния между координатами и получаю немного разные результаты в зависимости от используемого языка.

Часть расчета занимает вычисление cosine данного radian. Я получаю следующие результаты

// cos(0.8941658257446736)

// 0.6261694290123146 node
// 0.6261694290123146 rust
// 0.6261694290123148 go
// 0.6261694290123148 python
// 0.6261694290123148 swift
// 0.6261694290123146 c++
// 0.6261694290123146 java
// 0.6261694290123147 c

Я хотел бы попытаться понять, почему. Если вы смотрите мимо 16dp c - это единственный «правильный» ответ с точки зрения округления. Что меня удивило, так это python с другим результатом.

Эта небольшая разница усиливается в настоящее время, и для тысяч позиций она добавляет немаловажное расстояние.

Не совсем уверен, какэто дубликат. Кроме того, я больше прошу целостного ответа, чем языковой. У меня нет ученой степени в области вычислительной техники.

ОБНОВЛЕНИЕ Я принимаю, что, возможно, это слишком широкий вопрос, я полагаю, мне было любопытно, почему, поскольку мой фон не является CS. Я высоко ценю ссылки на блоги, которые были размещены в комментариях.

ОБНОВЛЕНИЕ 2

Этот вопрос возник при переносе службы с nodejs на go. Go еще более странно, так как теперь я не могу запускать тесты, поскольку сумма расстояний варьируется в зависимости от нескольких значений.

Учитывая список координат, вычисляя расстояние и складывая их вместе, я получаю разные результаты. Я не задаю вопрос, но кажется сумасшедшим, что go даст другие результаты.

9605.795975874069
9605.795975874067
9605.79597587407

Для полноты здесь приведен расчет расстояния, который я использую:

func Distance(pointA Coordinate, pointB Coordinate) float64 {
    const R = 6371000 // Earth radius meters
    phi1 := pointA.Lat * math.Pi / 180
    phi2 := pointB.Lat * math.Pi / 180
    lambda1 := pointA.Lon * math.Pi / 180
    lambda2 := pointB.Lon * math.Pi / 180

    deltaPhi := phi2 - phi1
    deltaLambda := lambda2 - lambda1
    a := math.Sin(deltaPhi/2)*math.Sin(deltaPhi/2) + math.Cos(phi1)*math.Cos(phi2)*math.Sin(deltaLambda/2)*math.Sin(deltaLambda/2)
    c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))

    d := R * c
    return d
}

1 Ответ

2 голосов
/ 16 октября 2019

Обычно представление чисел с плавающей запятой определяется стандартом IEEE 754 , и я предполагаю, что этот стандарт реализуется всеми (основными) языками программирования.

Точность и округление известны проблемы и иногда могут привести к неожиданным результатам .

Аспекты, которые могут влиять на результат вычисления в зависимости от языка программирования или используемой математической библиотеки:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...