точность длинного двойного sqrt () - PullRequest
4 голосов
/ 27 января 2011

Я заметил проблему с точностью длинной двойной версии sqrt (). Следующий код демонстрирует проблему.

#include <iostream>
#include <cmath>
#include <cfloat>

int main(int argc, char ** argv)
{
  int count=0;
  long double s=sqrt(3L);
  std::cout.precision(21);
  std::cout << "s=" << s << ", s^2=" << s*s << std::endl;
  while( s*s<3L+LDBL_EPSILON ) {
    s+=LDBL_EPSILON;
    std::cout << s << ' ' << s*s << std::endl;
    ++count;
  }
  std::cout << "sqrt(3L) is approximately " << count << " multiples of LDBL_EPSILON away from the correct value." << std::endl;
  return 0;
}

Компиляция и запуск с

>g++ -o sqrt sqrt.cpp && ./sqrt

дает

s=1.73205080756887719318, s^2=2.9999999999999996524
1.73205080756887719329 2.99999999999999965284
1.73205080756887719339 2.99999999999999965306
... (922 lines omitted)
1.73205080756887729347 2.99999999999999999978
1.73205080756887729357 3.00000000000000000022
sqrt(3L) is approximately 926 multiples of LDBL_EPSILON away from the correct value.

Обычная двойная версия sqrt () дает двойное значение, наиболее близкое к реальному значению.

Используемая версия g ++:

>g++ -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.5 (Debian 4.4.5-8) 

Это известная ошибка? Должен ли я сообщить об этом где-нибудь?

Ответы [ 2 ]

9 голосов
/ 27 января 2011

У вас есть две проблемы: во-первых, 3L неявно повышается до double, а не long double, поэтому, даже если вы присваиваете возвращаемое значение для long double, он по-прежнему использует низкую точность версии sqrt. Вам нужно будет от static_cast 3 до long double в качестве аргумента. Во-вторых, только double версия sqrt импортируется в глобальное пространство имен, поскольку перегрузка функций не поддерживается в C, вместо этого вы должны использовать std::sqrt.

Таким образом:

long double s=std::sqrt(static_cast<long double>(3));

0 голосов
/ 27 января 2011

Значение из 'обычной' версии double sqrt () испытывает большую степень детализации округления, чем long double? Это то, что мы ожидали. Может случиться так, что это «гранулярное» округление оказывается близко к правильному значению - ближе, чем длинный двойной квадрат.

Способ проверить это - попробовать несколько значений и сравнить.

...