有没有比加0.5f并截断转换更直接的方法将float转换为int并进行舍入?

2024-03-29

在处理浮点数据的 C++ 代码中,从 float 到 int 的舍入转换相当频繁。例如,一种用途是生成转换表。

考虑一下这段代码:

// Convert a positive float value and round to the nearest integer
int RoundedIntValue = (int) (FloatValue + 0.5f);

C/C++ 语言将 (int) 强制转换定义为截断,因此必须添加 0.5f 以确保向上舍入到最接近的正整数(当输入为正数时)。对于上述内容,VS2015的编译器生成以下代码:

movss   xmm9, DWORD PTR __real@3f000000 // 0.5f
addss   xmm0, xmm9
cvttss2si   eax, xmm0

上面的方法有效,但可能会更有效......

英特尔的设计人员显然认为这个问题足够重要,可以用一条指令来解决,该指令可以完成所需的工作:转换为最接近的整数值:cvtss2si(注意,助记符中只有一个“t”)。

如果 cvtss2si 替换上述序列中的 cvttss2si 指令,则将消除三个指令中的两个(就像使用额外的 xmm 寄存器一样,这可能会导致整体上更好的优化)。

那么我们如何编写 C++ 语句来使用一条 cvtss2si 指令完成这项简单的工作呢?

我一直在摸索,尝试类似以下的事情,但即使优化器正在执行任务,它也不会归结为可以/应该完成这项工作的一条机器指令:

int RoundedIntValue = _mm_cvt_ss2si(_mm_set_ss(FloatValue));

不幸的是,上面的内容似乎一心想要清除一整个永远不会使用的寄存器向量,而不是仅仅使用一个 32 位值。

movaps  xmm1, xmm0
xorps   xmm2, xmm2
movss   xmm2, xmm1
cvtss2si eax, xmm2

也许我在这里错过了一个明显的方法。

您能否提供一组建议的 C++ 指令来最终生成单个 cvtss2si 指令?


这是微软编译器的一个优化缺陷,该bug已经被报告了 https://connect.microsoft.com/VisualStudio/feedback/details/3118553到微软。正如其他评论员提到的,现代版本的 GCC、Clang 和 ICC 都会生成预期的代码 https://godbolt.org/g/hyWUct。对于这样的函数:

int RoundToNearestEven(float value)
{
   return _mm_cvt_ss2si(_mm_set_ss(value));  
}

除 Microsoft 之外的所有编译器都会发出以下目标代码:

cvtss2si  eax, xmm0
ret

而 Microsoft 的编译器(截至 VS 2015 Update 3)会发出以下内容:

movaps    xmm1, xmm0
xorps     xmm2, xmm2
movss     xmm2, xmm1
cvtss2si  eax,  xmm2
ret

双精度版本也是如此,cvtsd2si (i.e., the _mm_cvtsd_si32固有的)。

在优化器得到改进之前,没有更快的替代方案可用。幸运的是,当前生成的代码并不像看起来那么慢。移动和寄存器清除是最快的指令之一,其中一些指令可能可以仅在前端作为寄存器重命名来实现。它是当然比任何可能的替代方案都要快——通常快几个数量级:

  • 您提到的加 0.5 的技巧不仅会更慢,因为它必须加载常量并执行加法,而且在所有情况下也不会产生正确的舍入结果。

  • 使用_mm_load_ss将浮点值加载到一个固有的__m128结构适合与使用_mm_cvt_ss2si内在是一种悲观,因为它会导致内存溢出,而不仅仅是寄存器到寄存器的移动。

    (请注意,虽然_mm_set_ss is always对于 x86-64 更好,其中调用约定使用 SSE 寄存器来传递浮点值,我有偶尔观察到_mm_load_ss将在 x86-32 构建中生成比_mm_set_ss,但它高度依赖于多种因素,并且只有在复杂的代码序列中使用多个内在函数时才会被观察到。你的默认选择应该是_mm_set_ss.)

  • 替换为reinterpret_cast<__m128&>(value)(或道德上的等价物)_mm_set_ss内在的既不安全又低效。它会导致从 SSE 寄存器溢出到内存;这cvtss2si然后指令使用该内存位置作为其源操作数。

  • 宣布临时__m128结构和值初始化是安全的,但效率更低。在堆栈上为整个结构分配空间,然后每个槽填充 0 或浮点值。然后将该结构的内存位置用作源操作数cvtss2si.

  • The lrintC 标准库提供的函数系列应该可以满足您的需求,并且实际上可以编译为简单的cvt*一些其他编译器上的指令,但在 Microsoft 编译器上极其次优。它们永远不会内联,因此您总是要付出函数调用的成本。另外,函数内部的代码不是最优的。这两个 https://connect.microsoft.com/VisualStudio/feedback/details/1089355/cq-lrintf-doesnt-optimize-to-a-single-instruction-regardless-of-settings已经报告为错误 https://connect.microsoft.com/VisualStudio/feedback/details/1179496/vc-lrintf-is-376-times-longer-than-gcc-clang-version,但我们仍在等待修复。标准库提供的其他转换函数也存在类似的问题,包括lround和朋友。

  • x87 FPU 提供FIST/FISTP执行类似任务的指令,但 C 和 C++ 语言标准要求强制转换truncate,而不是舍入到最近的偶数(默认的 FPU 舍入模式),因此编译器有义务插入一堆代码来更改当前的舍入模式,执行转换,然后再改回来。这是极其速度很慢,并且除了使用内联汇编之外,没有办法指示编译器不这样做。除了 64 位编译器不提供内联汇编之外,MSVC 的内联汇编语法也无法指定输入和输出,因此您必须付出双倍的加载和存储代价。即使情况并非如此,您仍然需要支付将浮点值从 SSE 寄存器复制到内存,然后复制到 x87 FPU 堆栈的成本。

内部函数很棒,通常可以让您生成比编译器生成的代码更快的代码,但它们并不完美。如果您像我一样,发现自己经常分析二进制文件的反汇编,您会发现自己经常感到失望。不过,这里最好的选择是使用内在函数。

As for why优化器以它的方式发出代码,我只能推测,因为我不在 Microsoft 编译器团队工作,但我的猜测是因为许多其他cvt*指令具有代码生成器需要解决的错误依赖性。例如,一个cvtss2sd不修改目标 XMM 寄存器的高 64 位。这样的partial寄存器更新会导致停顿并减少指令级并行的机会。这在循环中尤其是一个问题,其中寄存器的高位形成第二个循环携带的依赖链,即使我们实际上并不关心它们的内容。因为执行cvtss2sd直到前面的指令完成后,指令才能开始,延迟大大增加。但是,通过执行xorss or movss首先,寄存器的高位被清除,从而打破依赖性并避免停顿的可能性。这是一个有趣的案例的例子shorter代码不等于faster代码。编译团队开始在 VS 2010 附带的编译器中插入这些用于标量转换的破坏依赖性的指令 https://blogs.msdn.microsoft.com/vcblog/2009/11/02/visual-c-code-generation-in-visual-studio-2010/,并且可能过度热衷于应用启发式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有没有比加0.5f并截断转换更直接的方法将float转换为int并进行舍入? 的相关文章

  • MSI 和 EXE 安装程序有什么区别,我应该选择哪一个? [复制]

    这个问题在这里已经有答案了 可能的重复 msi 和 setup exe 文件之间有什么具体区别 https stackoverflow com questions 1789530 what are the specific differen
  • 如何从该 Voronoi 图数据中获取单元格字典?

    使用找到的voronoi delaunay图生成库在这个节目中 http sourceforge net projects mapmanager 这是基于 财富 最初的实施他的算法 http en wikipedia org wiki Fo
  • C++ 模板中的名称查找

    我有一些 C 代码 如果没有 fpermissive 选项 就无法再编译 这是我无法分享的专有代码 但我认为我已经能够提取一个简单的测试用例来演示该问题 这是 g 的输出 template eg cpp In instantiation o
  • CMake 和 Visual Studio:如何获得快速、安静的命令行构建?

    我有一个 cmake 项目 它成功地完成了我想要的一切 但我有大约 100 个文件 当我只需要重新编译一个文件时 我厌倦了每次看到生成的巨大输出 每个文件 30 行 明确地说 我正在编译cmake build 得到这个结果 我需要传递给编译
  • 隐式方法组转换陷阱

    我想知道为什么给定代码的输出 在 LinqPad 中执行 void Main Compare1 Action Main Dump Compare2 Main Dump bool Compare1 Delegate x return x Ac
  • 如何“杀死”Pthread?

    我正在学习 Pthreads 并且想知道杀死这样一个对象的最佳方法是什么 在寻找类似的问题后 我无法找到 明确 的答案 但请随时向我指出任何相关问题 我正在使用一个小型客户端服务器应用程序 其中服务器主线程正在侦听套接字上的客户端连接 每次
  • 异步方法中的异常未被捕获

    下面的代码没有捕获我的OperationCancelEException 它是通过调用抛出的ct ThrowIfCancellationRequested public partial class TitleWindow Window IA
  • 基于 MS Bot Framework 中的响应分支对话框/表单

    我们正在尝试使用 MS Bot Framework 但尚未完全弄清楚如何实现此场景 我们有一个 LUIS 对话框 类型 它工作正常并且经过适当的培训 以常见的三明治为例 LUIS 意图寻找的基本内容是用户询问订单状态 如果问题中提供了订单号
  • C++ 私有静态成员变量

    此 C 代码在编译时产生链接器错误 A h class A public static void f private static std vector
  • Azure 2012 年 10 月 SDK 损坏 UseDevelopmentStorage=true

    有人尝试过使用 usedevelopmentstorage true 连接字符串的 2012 年 10 月 Azure sdk 吗 CloudStorageAccount Parse UseDevelopmentStorage true 抛
  • 替换 JSON 中的转义字符

    我想用空格替换 JSON 字符串中的 字符 我怎样才能做到这一点 我发现从 JSON 字符串中删除所有转义字符的最简单 最好的方法是将字符串传递到正则表达式 Unescape 方法 此方法返回一个没有转义字符的新字符串 甚至删除了 n t
  • 为什么C++变量是指针时不需要正确定义?

    我对 C 语言完全陌生 特别是指针 经验主要是 PHP 并且希望对以下内容进行一些解释 我已经尝试寻找答案 这两行代码如何能够在我的程序中完成完全相同的工作 第二行似乎违背了我迄今为止所学到和理解的关于指针的一切 char disk 3 D
  • std::string 在 Visual Studio 上的具体行为?

    我有一个项目需要读取 写入大文件 我决定使用 ifstream read 将这些文件一次性放入内存中 放入 std string 中 这似乎是在 C 中执行此操作的最快方法 http insanecoding blogspot com 20
  • 无法将方法组“Read”转换为非委托类型“bool”

    我正在尝试使用SqlDataReader检查条目是否存在 如果存在则返回ID 否则返回false 当我尝试编译时 出现错误 无法将方法组 Read 转换为非委托类型 bool 我一直在遵循在 VB 中找到的示例 但似乎翻译可能不正确 pri
  • C# 的空条件委托调用线程安全吗? [复制]

    这个问题在这里已经有答案了 这就是我一直以来编写事件引发者的方式 例如属性更改 public event PropertyChangedEventHandler PropertyChanged private void RaisePrope
  • C 中什么函数可以替换字符串中的子字符串?

    给定一个 char 字符串 我想查找所有出现的子字符串并将其替换为备用字符串 我没有看到任何简单的函数可以实现这一点
  • win32 API 和 .NET 框架之间的选择

    我必须开发一个适用于 Windows 的应用程序 该应用程序将能够通过网络摄像头识别手势来控制鼠标 我将使用 vc 2008 进行开发 但我很困惑是使用 NET 框架还是核心 win32 API 性能对于我的应用程序非常重要 根据 Ivor
  • 在类中使用 std::chrono::high_resolution_clock 播种 std::mt19937 的正确方法是什么?

    首先 大家好 这是我在这里提出的第一个问题 所以我希望我没有搞砸 在写这篇文章之前我用谷歌搜索了很多 我对编码 C 很陌生 我正在自学 考虑到有人告诉我 只为任何随机引擎播种一次是一个很好的做法 我在这里可能是错的 什么是正确 最佳 更有效
  • 致命错误 C1001:编译器中发生内部错误(编译器文件“msc1.cpp”,第 1325 行)

    当我编译代码时 错误指向以下类 该错误在两行上突出显示 如下所示 tm validFrom tm validUntil struct t SslCertData final struct t Contact TCHAR Organizati
  • 如何将 char 转换为 unsigned int?

    我有一个字符数组 它实际上用作字节数组 而不是用于存储文本 在数组中 有两个特定字节表示我需要存储到无符号 int 值中的数值 下面的代码解释了设置 char bytes bytes 2 bytes 0 0x0C For the sake

随机推荐

  • 单击鼠标更改 Knockout.js 中的 css 类

    Knockout js 文档显示了这样的 css 绑定 div Profit Information div 我需要对其进行调整以更改鼠标单击时的 css 类 我怎样才能做到这一点 根据下面的答案 我正在使用这样的代码 CSS class
  • Java内存不足错误(本机内存),达到进程大小限制(32位linux)

    我正在测试 Web 应用程序的性能 并收到 内存不足错误 本机内存 我测试了好几次 每次都是 无法为 Chunk new 分配 83886088 字节 并死掉 我每分钟打印一次内存大小 发现进程死亡前VmSize为2924700 kB 我认
  • 在Python中读取.dss数据库文件

    我有一个 DSS 数据库文件 我想使用 Python 从该文件中提取数据库架构 我实际上需要对此数据库执行查询 但我找不到任何好的文档来开始 所以 我决定如果我可以提取模式 我可以创建一个 SQLite 数据库并在那里运行我的实验 文件内容
  • Web API 2 返回 OK 响应但继续在后台处理

    我已经为 shopify 创建了一个 mvc web api 2 webhook public class ShopifyController ApiController PUT api Afilliate SaveOrder Respon
  • 单击外部时防止日期选择器关闭[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在使用 jquery datepicker 我想在单击日历对话框外部时阻止关闭功能 url http jqueryui com dat
  • 禁用下拉菜单中的选项 Unity

    I need to disable 1 or 2 dropdown option from a dropdown menu in Unity The dropdown menu should not be repopulated There
  • R、正确使用粘贴功能

    该循环正确创建了 13 个名为 bond1 bond13 的 df 并为它们分配了 function1 中的值 现在我需要使用 function2 和另外两个 df 创建另外 13 个名为 spread1 spread13 的 DF 其中之
  • 如何使用 PHP 检测访问我们网站的用户的 MAC 地址?

    我知道如何追踪 IP 地址 但我需要追踪查看我的页面的用户的 MAC 地址 如何做到这一点 除非用户与网络服务器位于同一 LAN 上 否则无法执行此操作 MAC 地址位于 TCP IP 下面的以太网层 并且不包含在从用户本地网络路由出去的
  • 为什么 SQL Server 2008 在长事务 INSERT 上阻止 SELECT?

    我们正在尝试建立一个只定期插入新记录的事务表 这个简单的表需要我们随着时间的推移不断向其中添加新记录 该表中的事务量预计会相当高 并且可能会定期批量导入事务 gt 1000 这可能需要几秒钟才能完成 然后 我们根据这些数据执行一组选择语句
  • 你能在 vim 中做交互式宏或录音吗?

    我想定义一个 vim 宏 在特定时间中断用户输入 这可能吗 编辑 原来是我的录音 q 而不是宏 可以在录音中使用输入命令 但麻烦大于其价值 我首先将插入输入转义映射到一个键 map
  • Visual Studio Code:如何同时打开调试窗口和资源管理器窗口?

    我是视觉代码新手 我有一个小问题 您有两个用红线圈出的按钮 第一个按钮打开资源管理器窗口 第二个圆圈按钮打开调试窗口 我想打开这两个窗口 你知道该怎么做吗 从 VSCode 1 13 开始这是不可能的 已跟踪显示多个面板的功能请求here
  • 使用TDD在Java中开发文件遍历代码

    我必须实现一些代码来遍历目录结构并返回找到的文件列表 要求非常简单 给定一个基本目录 查找其中的所有文件 不是目录本身 如果找到目录 请重复步骤 1 我想使用 TDD 开发代码 当我开始编写测试时 我意识到我在嘲笑班级File 这样我就可以
  • PHP 致命错误:内存不足(已分配 80740352)(试图分配 12352 字节)

    当用户在我的网站上上传图像时 我收到此错误 错误消息是 PHP 致命错误 home 内存不足 已分配 80740352 试图分配 12352 字节 如何使用 php ini 修复此问题 这是我当前的上传 php ini 设置 upload
  • 对 .toolbarBackground SwiftUI 使用渐变

    我正在尝试为导航 toolbarBackground 设置自定义渐变 但任何时候我都只使用 LinearGradient 中的第一种颜色运行它 toolbar ToolbarItem placement navigationBarTrail
  • 在 React Native 地图中设置最大和最小缩放级别

    如何在 React Native 地图中设置最小和最大缩放级别
  • 如何从 EditText 中仅获取可见文本

    我有一个用于在 Android 上运行的代码编辑器的 EditText 我正在对内容应用语法突出显示 除了 EditText 滚动到屏幕之外的大字符串之外 它运行良好 每次按键时语法突出显示都会应用于整个 Spannable 有谁知道我如何
  • 核心数据实体唯一约束不起作用

    我正在尝试使用新的实体约束检查器在核心数据中设置约束 以使项目的名称唯一 我读过的所有内容都表明这非常简单 设置约束并处理错误 我没有收到任何错误 并且可以根据需要多次添加相同的条目 该应用程序确实需要 IOS 9 0 Xcode 工具要求
  • 在 VSTS 上调用 Pull Request API 时收到“403 Forbidden”

    对于我调用的其他 API 包括 Analytics 一切似乎都工作正常 但是当我尝试进行任何拉取请求查询时 我得到一个403 Forbidden 这些呼叫是由我的私人 Windows 代理进行的 其 PAT 已获得所有范围的授权 它正在使用
  • 如何四舍五入到最接近的十? [复制]

    这个问题在这里已经有答案了 如何在没有 if 语句的情况下将数字四舍五入到最接近的十 例如 98 到 100 int num 87 double t double d 1 0 num d 87 0 t d 100 System out pr
  • 有没有比加0.5f并截断转换更直接的方法将float转换为int并进行舍入?

    在处理浮点数据的 C 代码中 从 float 到 int 的舍入转换相当频繁 例如 一种用途是生成转换表 考虑一下这段代码 Convert a positive float value and round to the nearest in