c++ log 函数使用浮点精度
当我给下面的函数一个非常接近 1.0 的数字时,我遇到了一个有趣的段错误。特别是当数字以浮点精度舍入为 1.0 时。
double get_random_element(double random_number)
{
if (random_number <= 0.0 || random_number >= 1.0)
throw std::runtime_error("Can't have a random number not on the range (0.0, 1.0)");
return -log(-log(random_number));
}
如果 random_number 为 1.0,则 log(1.0) = 0.0,并且 log 为零是导致段错误的未定义计算。然而我本以为第一行的错误检查会阻止这种情况发生。 Ddebugging 显示非常接近 1 的数字将通过错误检查,但无论如何都会从 log 函数返回 0,这让我相信 log 函数仅使用单浮点精度。
我的包含内容如下,所以我只能假设我正在使用 math.h
#include <string>
#include <math.h>
#include <sstream>
#include <map>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
#include <utility>
更新中的日志:正如所指出的,一个简单的解决方案是仅使用浮点数作为参数,并且如果传入等于 1.0f 的数字只需删除 std::numeric_limits::epsilon() 以给出可以安全地传递到双日志中的数字。
但我想回答的问题是为什么调用接近但不等于 1 的数字的双对数会失败。
更新 2:在测试项目中重新创建此问题后,我认为问题实际上出在输入中。如果我通过了,
double val = 1.0 - std::numerical_limits<double>::epsilon();
我的功能就没有问题。然而,我实际上传入的是
boost::mt19937 random_generator;
double val = (random_generator()+1)/4294967297.0;
random_generator 被设计为返回 [0, 2^32 - 1] == [0,4294967295] 范围内的数字。所以我决定输入最大可能的返回值
double val = (4294967295+1)/4294967297.0;
,这很快就给了我一个关于 unsigned int 溢出的警告,并且果然生成了一个零。我正在重新编译以下内容:
get_random_element((random_generator()+1.0)/4294967297.0);
并希望这种奇怪的行为能够得到解决。
更新3:我终于找到了这里发生的事情......和往常一样,它归结为用户错误(我自己就是错误)。还有第二个控制路径通向此方法,该方法暂时将双精度值存储为浮点数,然后将其转换回双精度,导致 0.999999999 四舍五入到 1.0,然后传递到 -log(-log(x)) 函数并导致它会摔倒。我仍然不明白的是,为什么我的检查
if (random_number <= 0.0 || random_number >= 1.0) throw runtime_error(blah)
在传递到日志函数之前没有捕获错误的输入?
I am having an interesting seg fault in the following function when I give it a number very close to 1.0. Specifically when the number would be rounded to 1.0 at FLOATING POINT precision.
double get_random_element(double random_number)
{
if (random_number <= 0.0 || random_number >= 1.0)
throw std::runtime_error("Can't have a random number not on the range (0.0, 1.0)");
return -log(-log(random_number));
}
If random_number is 1.0 then log(1.0) = 0.0 and the log of zero is an undefined calculation leading to a seg fault. However I would have thought that the error checking on the first line would have prevented this from ever happening. Ddebugging shows that a number very close to 1 will pass through the error checking but return 0 from the log function anyway leading me to believe that the log function is using only single floating point precision.
my includes are as follows so i can only assume I'm using the log from math.h
#include <string>
#include <math.h>
#include <sstream>
#include <map>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
#include <utility>
UPDATE: As pointed out an easy solution is to just use a floating point number as the argument and if a number equal to 1.0f is passed in to just remove std::numeric_limits::epsilon() to give a number which can be safely passed into the double log.
But the question I'd like answered is why does calling double log of a number near but not equal to 1 fail.
UPDATE 2: After recreating this problem in a test project I think the problem is actually in the inputs. If I pass in
double val = 1.0 - std::numerical_limits<double>::epsilon();
I have no problems with the function. However what I actually pass in is
boost::mt19937 random_generator;
double val = (random_generator()+1)/4294967297.0;
where random_generator is designed to return a number on the range [0, 2^32 - 1] == [0,4294967295]. So I decided to punch in the largest possible return value
double val = (4294967295+1)/4294967297.0;
which quickly gave me a warning about unsigned int overflow and sure enough generated a zero. I am recompiling with the following:
get_random_element((random_generator()+1.0)/4294967297.0);
and hopefully this strange behaviour will be resolved.
UPDATE 3: I have finally found what is going on here... and as usual it comes down to user error (myself being the error). There was a second control path leading to this method which temporarily stored the double value as a float and then converted it back to double leading to 0.999999999 being rounded to 1.0 and then passed into the -log(-log(x)) function and causing it to fall over. What I still don't understand is why my checking
if (random_number <= 0.0 || random_number >= 1.0) throw runtime_error(blah)
didn't catch the erroneous input before it was passed into the log functions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为 quamrana 有一个很好的观点(它也立即引起了我的注意)。然而,我已经能够运行这个片段相当长的时间:
例如:
也许你可以使用类似的东西?
I think quamrana has a good point (it immediately drew my attention too). However, I've been able to run this snippet for considerable length:
E.g.:
Perhaps you could use something similar in vein?