用ldexp反转frexp [英] Inverting frexp with ldexp
问题描述
如果我了解文档
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>();
}
在Ubuntu 14.04.3 LTS上使用g++
(版本4.8.4)进行编译时,输出为:
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)
使用long double
时,我们似乎通过将x
与frexpr
分解而丢失了一些东西.如果使用python3
(版本3.4.3)运行以下脚本,则可以实现我期望的行为.
When using long double
s 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))
输出为:
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)
请注意,这仅使用double
.
我试图读取cmath
头文件,以了解frexpr
和ldexpr
的实现方式,但我无法理解.发生了什么事?
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?
推荐答案
通过包括typeinfo
标头并在test_ldexp
函数中对compare
的调用之前插入以下行,来修改C ++程序.>
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;
现在输出为:
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
和ldexpr
都将返回double
!看来您正在使用math.h
中定义的功能(请参见此处和此处),而不是在cmath
中定义的内容.将对frexpr
和ldexpr
的调用替换为对std::frexpr
和std::ldexpr
的调用,您的代码将按预期工作.
frexpr
and ldexpr
are returning double
s 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.
这篇关于用ldexp反转frexp的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!