static_cast<uint32_t> 对于非 const/constexpr 浮点返回 0,保持 2^32 -1 (UINT32_MAX),但当浮点为 const/constexpr 时返回 UINT32_MAX

问题描述 投票:0回答:2

我在尝试将一段代码放入模板函数中时遇到了问题。该代码基本上执行钳位。下面是代码的可重现版本。

#include<iostream>

int main(){
    

  constexpr float lower_value{0.0F};
  constexpr float upper_value{4294967295.0F};
  float value{4294967295.0F};
  uint32_t clampedValue{0};
  
  if (value <= lower_value) {
    clampedValue = static_cast<decltype(clampedValue)>(lower_value);
  } else if (value >= upper_value) {
    clampedValue = static_cast<decltype(clampedValue)>( upper_value);
  } else {
     clampedValue = static_cast<decltype(clampedValue)>(std::round(value));
  }
 
  std::cout << clampedValue << std::endl;
  return 0;

} 

只要

lower_value
upper_value
const
constexpr
,就可以正常工作。否则它输出
0

当我运行以下几行时,它分别给出了

0
4294967295
4294967295
0
的原因是浮点转换错误,因为
4294967295
增加了
1
4294967296
以适合23位尾数,但这超出了Uint32的范围,并且发生了回绕,在之后给出了
0
static_cast<uint32_t>
。但是为什么使用
const float
constexpr float
时会得到正确的结果?

float value{4294967295.0F};
constexpr float valueConstexpr{4294967295.0F};
const float valueConst{4294967295.0F};
        
std::cout << static_cast<uint32_t>(value) << std::endl;
std::cout << static_cast<uint32_t>(valueConstexpr) << std::endl;
std::cout << static_cast<uint32_t>(valueConst) << std::endl;

上述

if else
程序的
main
逻辑被放入一个模板函数中,其形状如下。

#include <iostream>
template <typename A, typename B, typename C, typename D>
constexpr auto process_value(const B& inVal, const C& lower_bound, const D& upper_bound) {

  A outVal;
  if (inVal <= static_cast<B>(lower_bound)){
    outVal = static_cast<A>(lower_bound);
  } else if (inVal >= static_cast<B>(upper_bound)){
    outVal = static_cast<A>(upper_bound);
  } else {
    outVal = static_cast<A>(std::round(inVal));
  }
  return outVal;
}

int main(){
    
  constexpr float lower_value{0.0F};
  constexpr float upper_value{4294967295.0F};
  float value{4294967295.0F};
  uint32_t clampedValue{0};
  
  clampedValue = process_value<decltype(clampedValue), float, float, float>(value, lower_value, upper_value);

  std::cout << clampedValue << std::endl;
  return 0;
}

但这也给出了

0
作为输出而不是
4294967295

我希望两个版本都能给出相同的输出。有人可以帮助我如何修复模板函数,使其与原始代码中的完全一样吗?

c++ templates floating-point constexpr static-cast
2个回答
0
投票

假设

float
是 IEEE 754 表示。

std::uint32_t
可以保真度保存的最大
float
4294967040.0f。 将
float
转换为
std::uint32_t
是明确定义的行为。

浮点数可以保存的下一个值是4294967296.0f,它超出了

std::uint32_t
的范围。 将
float
值转换到
std::uint32_t
范围之外是 未定义行为,应该避免。

为什么?因为

float
具有 24 位精度有效数(有时称为尾数,尽管用词不当)。

最高的单位增量

float
值为16777216.0f,此后存在可表达性的“单位值差距”。 有时这也是需要注意的一点信息。

要修复原始发布的代码,请更改 upper_value:

constexpr float upper_value{ 4294967240.0f };

0
投票

static_cast<float>(std::numeric_limits<std::uint32_t>::max())
是 4294967296,比 4294967295 大 1,并且不再适合 32 位无符号整数。 Godbolt 演示

原因是 float 的尾数只有 23 位,因此它无法准确表示适合

uint32_t
的所有整数。因此它会圆形。例如,4294967167 将四舍五入为 4294967040,而 4294967168 已四舍五入为 4294967296。

因此,如果您将限制转换为浮动,然后转换为夹紧,这将会失败。

您可以使用

std::nextafter(static_cast<float>(std::numeric_limits<std::uint32_t>::max()), -std::numeric_limits<float>::infinity())
获取仍然适合两种数据类型的浮点值之前的浮点值,即 4294967040。任何大于该值的
float
值都会自动四舍五入为不适合的值在
uint32_t

© www.soinside.com 2019 - 2024. All rights reserved.