2016-02-11 2 views
0

проблематичен выход FMOD (длинный двойной, длинный двойной)проблематичен выход FMOD (длинный двойной, длинный двойной)

кажется, что выход FMOD (длинный двойной, длинный двойной) в этом тесте problematocs. Любые предложения?

г ++ --version г ++ (GCC) 4.9.2

uname -srvmpio CYGWIN_NT-6,1 1.7.34 (0,285/5/3) 2015-02-04 12:12 i686 неизвестно неизвестно Cygwin

г ++ test1.cpp // нет ошибок, нет предупреждений

./a.exe

l1 = 42 94967296

l2 = 72057594037927934

l3 = 4294967294

d1 = 4294967296

d2 = 72057594037927934

d3 = 0 // Ожидаемое 4294967294

// -------- Program test1. cpp -------- 
#include <iostream> 
#include <iomanip> 
#include <cmath> 

int main (int argc, char** argv) 
{ 

    long long l1 = 4294967296; 
    long long l2 = 72057594037927934; 
    long long l3 = l2 % l1; 

    long double d1 = static_cast<long double>(l1); 
    long double d2 = static_cast<long double>(l2); 
    long double d3 = fmod (d2, d1); 

    std::cout << "l1 = " << l1 << std::endl; 
    std::cout << "l2 = " << l2 << std::endl; 
    std::cout << "l3 = " << l3 << std::endl; 

    std::cout << std::endl; 
    std::cout << "d1 = " << std::setprecision(18) << d1 << std::endl; 
    std::cout << "d2 = " << std::setprecision(18) << d2 << std::endl; 
    std::cout << "d3 = " << std::setprecision(18) << d3 << std::endl; 

    return 0; 

} 
// ----------------------- 

ответ

0

Типы с плавающей точкой, включая long double, не могут представлять все интегральные значения в своем диапазоне. На практике они менее склонны поддерживать более высокие значения величины.

Последствием является то, что преобразование больших интегральных значений в long double (или любой тип с плавающей точкой) не обязательно сохраняет значение - преобразованное значение является лишь ближайшим приближением возможных заданных пределов типа с плавающей точкой.

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

fmod() также не перегружен, чтобы принять аргументы long double. Это означает, что ваши значения long double будут преобразованы в double. Набор значений a double может представлять собой подмножество множества, которое может представлять long double. Между прочим, это означает меньший диапазон интегральных значений, которые могут быть представлены точно.

Обычное предложение - не делать такие вещи. Если вы можете выполнить требуемые операции с использованием целых операций (например, %), тогда сделайте это. И если вы используете плавающие точки, вам необходимо разрешить и управлять потерей точности, связанной с использованием с плавающей запятой.

+0

d2 содержит 17 десятичных цифр. std :: numeric_limits :: digits10 == 18. Итак, кажется, что d3 должно быть действительным значением. – Alex

+0

А, ладно. Существует также тот факт, что 'fmod()' принимает только аргументы типа 'double' и возвращает' double'. Я добавлю короткую заметку об этом. – Peter

+0

FWIW, fmodl() принимает длинные удвоения и дает гораздо более точный ответ, d4 = fmodl (d2, d1) - 4294967294. Протестировано с g ++ 4.8.4 на Ubuntu 14.04, на машине x86_64. –

Смежные вопросы