Inverting frexp with ldexp

女生的网名这么多〃 提交于 2019-12-11 22:46:43

问题


If I understand the documentation correctly, we should be able to use ldexp to recover a floating point number decomposed into a signed mantissa and an exponent by frexp. I have been unable to achieve this. Consider the following code:

#include <cmath>
#include <iostream>
#include <limits>

template <typename T>
void float_info() {
    std::cout << "max=" << std::numeric_limits<T>::max()          <<
        ", max_exp="    << std::numeric_limits<T>::max_exponent   <<
        ", max_10_exp=" << std::numeric_limits<T>::max_exponent10 <<
        ", min="        << std::numeric_limits<T>::min()          <<
        ", min_exp="    << std::numeric_limits<T>::min_exponent   <<
        ", min_10_exp=" << std::numeric_limits<T>::min_exponent10 <<
        ", dig="        << std::numeric_limits<T>::digits10       <<
        ", mant_dig="   << std::numeric_limits<T>::digits         <<
        ", epsilon="    << std::numeric_limits<T>::epsilon()      <<
        ", radix="      << std::numeric_limits<T>::radix          <<
        ", rounds="     << std::numeric_limits<T>::round_style    << std::endl;
}

template <typename T>
void compare(T a, T b) {
    std::cout << a << " " << b << " (" <<
        (a != b ? "un" : "") << "equal)" << std::endl;
}

template<typename T>
void test_ldexp() {
    float_info<T>();

    T x = 1 + std::numeric_limits<T>::epsilon();
    T y = ldexp(x, 0);
    int exponent;
    T mantissa = frexp(x, &exponent);
    T z = ldexp(mantissa, exponent);

    compare(x, y);
    compare(x, z);

    std::cout << std::endl;
}

int main() {
    std::cout.precision(25);
    test_ldexp<float>();
    test_ldexp<double>();
    test_ldexp<long double>();
}

When compiled with g++ (version 4.8.4) on Ubuntu 14.04.3 LTS, the output is:

max=3.402823466385288598117042e+38, max_exp=128, max_10_exp=38,
min=1.175494350822287507968737e-38, min_exp=-125, min_10_exp=-37, dig=6,
mant_dig=24, epsilon=1.1920928955078125e-07, radix=2, rounds=1
1.00000011920928955078125 1.00000011920928955078125 (equal)
1.00000011920928955078125 1.00000011920928955078125 (equal)

max=1.797693134862315708145274e+308, max_exp=1024, max_10_exp=308,
min=2.225073858507201383090233e-308, min_exp=-1021, min_10_exp=-307, dig=15,
mant_dig=53, epsilon=2.220446049250313080847263e-16, radix=2, rounds=1
1.000000000000000222044605 1.000000000000000222044605 (equal)
1.000000000000000222044605 1.000000000000000222044605 (equal)

max=1.189731495357231765021264e+4932, max_exp=16384, max_10_exp=4932,
min=3.362103143112093506262678e-4932, min_exp=-16381, min_10_exp=-4931, dig=18,
mant_dig=64, epsilon=1.084202172485504434007453e-19, radix=2, rounds=1
1.00000000000000000010842 1 (unequal)
1.00000000000000000010842 1 (unequal)

When using long doubles we appear to be losing something by decomposing our x with frexpr. I can achieve the behavior I expect if I run the following script with python3 (version 3.4.3).

import math
import sys

def compare(a, b):
    print('{a} {b} ({pre}equal)'.format(a=a, b=b,
        pre='un' if a != b else ''))

x = 1 + sys.float_info.epsilon
mantissa, exponent = math.frexp(x)

print(sys.float_info)
compare(x, math.ldexp(x, 0))
compare(x, math.ldexp(mantissa, exponent))

The output is:

sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308,
min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15,
mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
1.0000000000000002 1.0000000000000002 (equal)
1.0000000000000002 1.0000000000000002 (equal)

Note that this is only using doubles.

I tried to read the cmath header file to understand how frexpr and ldexpr are implemented, but I couldn't make sense of it. What is going on?


回答1:


Modify your C++ program by including the typeinfo header and inserting the following lines right before the calls to compare in your test_ldexp function.

std::cout << "types:" << std::endl;
std::cout << "  x         : " << typeid(x).name() << std::endl;
std::cout << "  mantissa  : " << typeid(frexp(x, &exponent)).name()
          << std::endl;
std::cout << "  ldexp(...): " << typeid(ldexp(x, 0)).name() << std::endl;
std::cout << "  ldexp(...): " << typeid(ldexp(mantissa, exponent)).name()
          << std::endl;

The output is now:

max=3.402823466385288598117042e+38, max_exp=128, max_10_exp=38,
min=1.175494350822287507968737e-38, min_exp=-125, min_10_exp=-37, dig=6,
mant_dig=24, epsilon=1.1920928955078125e-07, radix=2, rounds=1
types:
  x         : f
  mantissa  : d
  ldexp(...): d
  ldexp(...): d
1.00000011920928955078125 1.00000011920928955078125 (equal)
1.00000011920928955078125 1.00000011920928955078125 (equal)

max=1.797693134862315708145274e+308, max_exp=1024, max_10_exp=308,
min=2.225073858507201383090233e-308, min_exp=-1021, min_10_exp=-307, dig=15,
mant_dig=53, epsilon=2.220446049250313080847263e-16, radix=2, rounds=1
types:
  x         : d
  mantissa  : d
  ldexp(...): d
  ldexp(...): d
1.000000000000000222044605 1.000000000000000222044605 (equal)
1.000000000000000222044605 1.000000000000000222044605 (equal)

max=1.189731495357231765021264e+4932, max_exp=16384, max_10_exp=4932,
min=3.362103143112093506262678e-4932, min_exp=-16381, min_10_exp=-4931, dig=18,
mant_dig=64, epsilon=1.084202172485504434007453e-19, radix=2, rounds=1
types:
  x         : e
  mantissa  : d
  ldexp(...): d
  ldexp(...): d
1.00000000000000000010842 1 (unequal)
1.00000000000000000010842 1 (unequal)

frexpr and ldexpr are returning doubles no matter what type you put in! It appears that you are using the functions defined in math.h (see here and here) instead those defined in cmath. Replace your calls to frexpr and ldexpr with calls to std::frexpr and std::ldexpr and your code will work as you expect.



来源:https://stackoverflow.com/questions/33314696/inverting-frexp-with-ldexp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!