【问题标题】:results from convert DBL_MAX to int is different from std::numeric_limits<double>::max() to int将 DBL_MAX 转换为 int 的结果不同于 std::numeric_limits<double>::max() 到 int
【发布时间】:2015-07-31 14:12:56
【问题描述】:

在进行转换测试时,我在 C++ 中遇到了一些奇怪的行为。

上下文

在线C++参考指出std::numeric_limits&lt;double&gt;::max()(定义在limit.h)的返回值应该是DBL_MAX(定义在float.h)。在我的测试中,当我打印出这些值时,两者确实完全相同。但是,当我将它们从double 转换为int 时,出现了奇怪的事情。

“相同”的输入,不同的结果?

int32_t t1 = (int) std::numeric_limits&lt;double&gt;::max();t1 设置为INT_MIN,但int32_t t2 = (int) DBL_MAX;t2 设置为INT_MAX。使用static_cast&lt;int&gt; 完成转换时也是如此。

'Same' 输入,相同情况下相同结果

但是,如果我定义一个函数

int32_t doubleToInt(double dvalue) {
    return (int) value;
}

doubleToInt(std::numeric_limits&lt;double&gt;::max())doubleToInt(DBL_MAX) 都返回 INT_MIN

为了帮助理解事物,我用 Java 实现了一个类似的程序。在那里,无论是否在函数中,所有类型转换都返回 INT_MAX 的值。

有人能指出为什么在 C++ 中结果在某些情况下是 INT_MIN,而在其他情况下是 INT_MAX 的原因吗?在 C++ 中将 DBL_MAX 转换为 int 时的预期行为应该是什么样的?

C++ 示例代码

#include <iostream>
#include <limits>
#include <float.h>
#include <stdlib.h>
#include <stdio.h>

using namespace std;

template <typename T, typename D> D cast(T a, D b) { return (D) a;}

int main()
{
    int32_t t1 = 9;
    std::cout << std::numeric_limits<double>::max() << std::endl;
    std::cout << DBL_MAX << std::endl;
    std::cout << (int32_t) std::numeric_limits<double>::max() << std::endl;
    std::cout << (int32_t) DBL_MAX << std::endl;
    std::cout << cast(std::numeric_limits<double>::max(), t1) << std::endl;
    std::cout << cast(DBL_MAX, t1) << std::endl;

    return 0;
}

为了完整性:我使用的是 cygwin gcc 和 java 8。

【问题讨论】:

  • @NathanOliver 我尝试了您发布的网站上的代码,它给出了相同的答案,我注意到它使用的是 g++ c++14。这是否意味着这个问题在一些旧版本的编译器上是孤立的?
  • 有可能。我在没有 c++11/14 支持的情况下重新编译,结果是一样的。奇怪的是,clang++ 给出的转换值与 g++ 不同:coliru.stacked-crooked.com/a/a686bcf40f6b1d59
  • @NathanOliver 我发现使用在线编译器编译c++代码时有优化选项。这是另一个支持在线编译的网站cpp.sh,在该网站的底部,您可以选择“优化级别”。如果我选择“none”,无论哪个 c++ 标准,它都会显示我描述的行为。这个网站tutorialspoint.com/compile_cpp_online.php 会给出同样的结果。

标签: c++ casting type-conversion double max


【解决方案1】:

尝试将大于INT_MAX 的浮点数转换为int未定义的行为

浮点类型的纯右值可以转换为整数类型的纯右值。转换截断;也就是说,小数部分被丢弃。如果截断的值不能是未定义的行为 以目标类型表示。 (§4.9 [conv.fpint],第 1 段)

因此,编译器可以为转换生成任何值(甚至可以执行其他操作,例如抛出异常)。不同的编译器可以做不同的事情。同一个编译器可以在不同的时间做不同的事情。

试图理解为什么一个特定的未定义行为实例会显示它显示的结果是没有意义的(除非你试图对编译器进行逆向工程,即使这样,UB 通常也不是特别有趣)。相反,您需要专注于避免未定义的行为。

例如,由于将浮点值转换为整数的任何超出范围的转换都是未定义的,因此您需要确保此类转换不涉及超出范围的值。与其他一些语言 [注 1] 不同,C++ 标准没有提供可测试的易于识别的结果,因此您需要在进行强制转换之前进行测试。


注意DBL_MAX 是一个宏,它的替换是一个字符串,表示最大可表示浮点数的近似值。另一方面,std::numeric_limits&lt;double&gt;::max() 是精确的最大可表示浮点数。

这种差异通常不会很明显,但是(如 §5.20 [expr.const] 标准中的注释所示,第 6 段):

由于本国际标准对浮点运算的准确性没有任何限制,因此未指定在翻译期间对浮点表达式的求值是否与对同一表达式的求值产生相同的结果(或在相同的值)在程序执行期间。

虽然std::numeric_limits&lt;double&gt;::max() 被声明为constexpr,但转换为int不是一个常量表达式(根据§5.20/p2.5),正是因为它的行为是未定义的。


注意事项

  1. 例如,在 Java 中,转换是明确定义的。详情请参阅Java Language Specification

【讨论】:

  • 谢谢rici,可以给我这本书的链接吗?可以免费看吗?
  • @TerryZ:C++ 标准的当前草案可通过标准委员会的网站isocpp.org/std 获得。 (链接在左栏的顶部。)阅读起来并不容易:)
猜你喜欢
  • 2012-12-01
  • 2015-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-08
  • 2015-08-30
  • 2011-05-10
相关资源
最近更新 更多