找回密码
 欢迎注册
查看: 32134|回复: 34

[分享] 发现微软编译器三路比较运算(<=>)的一个大 bug!

[复制链接]
发表于 2020-3-17 14:42:28 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?欢迎注册

×
有符号的大整数加减法,最终都会进行无符号的加减法。
而无符号的减法,必须得是较大者作为被减数,也就是说得先有个比较运算。

而就是这一步,曾害得我花了好几个小时去排查一个 bug,最终居然是微软编译器的问题!
  1. // test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #include "pch.h"

  4. #include <compare>
  5. #include <iostream>

  6. // 2020-03-16, gxqcn 发现编译器 bug: 如写成 const auto cmp{ *--pl <=> *--pr }, 当 0 != cmp 时,指针会多移动一次!
  7. int main()
  8. {
  9.     const auto test{ []( auto a, auto b )
  10.     {
  11.         std::cout << "before compare: a=" << a << " b=" << b << "\n";
  12.         const auto cmp{ a++ <=> b++ };
  13.         std::cout << "after  compare: a=" << a << " b=" << b << "\n\n";

  14.         // 为了对比检测 std::max() 是否有类似表现,
  15.         std::cout << "std::max( a++, b++ ) = " << std::max( a++, b++ ) << "\n";
  16.         std::cout << "after  std::max: a=" << a << " b=" << b << "\n\n";
  17.     }};

  18.     test( 1, 1 );   // 测试表明:当相等时,表现与预期相符
  19.     test( 1, 2 );   // 测试表明:当不等时,表现与预期不符

  20.     std::cout << "-------------------------\n\n";

  21.     int v1[]{ 1, 2, 3, 4, 5, 6 };
  22.     int v2[]{ 1, 2, 0, 9, 0, 6 };

  23.     auto p1{ std::cbegin( v1 ) - 1 };
  24.     auto p2{ std::cbegin( v2 ) - 1 };

  25.     std::cout << "compare: v1{ 1, 2, 3, 4, 5, 6 } <=> v2{ 1, 2, 0, 9, 0, 6 }\n\n";

  26.     for ( auto index{ 0 }; sizeof( v1 ) / sizeof( v1[ 0 ] ) != index; ++index )
  27.     {
  28.         if ( const auto cmp{ *++p1 <=> *++p2 }; 0 != cmp )
  29.         {
  30.             std::cout << "v1[" << index << "] = " << v1[ index ] << "; *p1 =" << *p1 << "\n";
  31.             std::cout << "v2[" << index << "] = " << v2[ index ] << "; *p2 =" << *p2 << "\n";

  32.             if ( 0 < cmp )
  33.             {
  34.                 std::cout << "\nv1 > v2";
  35.             }
  36.             else
  37.             {
  38.                 std::cout << "\nv1 < v2";
  39.             }

  40.             if (( v1[ index ] <=> v2[ index ] ) == cmp )
  41.             {
  42.                 std::cout << ", it's right.\n\n";
  43.             }
  44.             else
  45.             {
  46.                 std::cout << " ?! it's wrong!!\n\n";
  47.             }

  48.             break;
  49.         }
  50.     }

  51.     system( "pause" );
  52. }
复制代码

注意:需在 属性页->常规->C++语言标准 中设定“预览 - 最新 C++ 工作草案中的功能 (std:c++latest)”才能编译

其运行结果如下:
  1. before compare: a=1 b=1
  2. after  compare: a=2 b=2

  3. std::max( a++, b++ ) = 2
  4. after  std::max: a=3 b=3

  5. before compare: a=1 b=2
  6. after  compare: a=3 b=4

  7. std::max( a++, b++ ) = 4
  8. after  std::max: a=4 b=5

  9. -------------------------

  10. compare: v1{ 1, 2, 3, 4, 5, 6 } <=> v2{ 1, 2, 0, 9, 0, 6 }

  11. v1[2] = 3; *p1 =4
  12. v2[2] = 0; *p2 =9

  13. v1 < v2 ?! it's wrong!!

  14. 请按任意键继续. . .
复制代码

这个 bug 不仅是运算结果不对的问题,还有导致指针越界的风险,
解决的方式是:在 <=> 两端,尽量避免自增/自减等运算
不过这只是避坑而已,坑仍在那里!

这个 bug 是昨天上午发现的,当时版本是 VS2019 16.4.6;
今早微软对它进行了更新,版本为 16.5.0;
但刚才测试了下,该 bug 依然存在。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-3-17 15:46:52 | 显示全部楼层
软件能不升级就不升级,能干活第一,新版本的软件,基本就是拿别人当小白鼠,
软件追求稳定能干活第一!
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-3-17 15:52:59 | 显示全部楼层
你不是开发者,不要来掺乎;拿别人做小白鼠,看是源于态度还是做事严谨性,至少微软不是,我也不是。
我发这个帖子,只是为了提醒其他人的。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-3-17 16:16:01 | 显示全部楼层
gxqcn 发表于 2020-3-17 15:52
你不是开发者,不要来掺乎;拿别人做小白鼠,看是源于态度还是做事严谨性,至少微软不是,我也不是。
我发 ...

虽然我不是开发者,但是我也经常码代码呀!
新版本的软件就是拿别人当小白鼠,微软出新操作系统都是
先发个版本出来,拿别人当小白鼠的,
活的时间长了,我现在变保守了,用的程序软件,
能不升级就不升级,程序软件,能稳定地干活,才是第一选择!
新功能永远最多只能排第二位!
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-3-17 16:21:39 | 显示全部楼层
gxqcn 发表于 2020-3-17 15:52
你不是开发者,不要来掺乎;拿别人做小白鼠,看是源于态度还是做事严谨性,至少微软不是,我也不是。
我发 ...

mathematica倒是真的有bug,
https://bbs.emath.ac.cn/forum.ph ... 977&fromuid=865
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-3-18 09:27:10 | 显示全部楼层
几乎所有人都同意,c++是最复杂的语言.c++20相对于c++98,几乎就是一门新语言.因为其复杂,学习成本太大,大量C++项目是基于旧的标准编写的,特别是一些历史项目.另外,因为其复杂,图书的更新也比较慢.目前,别说是c++17,c++19方面的书了,就是将c++11的图书也没几本.语言的标准,不建议使用最新的.对于最新的标准,编译器支持的一般比较有限,换个编译器,可能就出问题.目前,我公司推荐用C++11标准.太新的标准,学习起来真的吃不消.

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-3-18 09:52:49 | 显示全部楼层
我觉得 C++ 标准进化最大的版本是 C++11,当然其经历的时间跨度也最大,之后的只是增补一些标准,使之更方便。
由于近几年标准更新得比较快,图书相对会跟不上;但可通过阅读相关的技术标准网站,还是能迅速跟上的。

以前要重载比较运算符有六种(<,<=,>,>=,==,!=);用新标准后,仅需重载两个即可:(<=>, == )
一般来说,用新的标准写代码,代码可以更优雅更自然更易读,可提效防低级失误,这何尝不是一种生产力?
当然,对于新生事物,也要保持一点警惕之心,需多加测试。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-3-18 12:41:04 | 显示全部楼层
gxqcn 发表于 2020-3-18 09:52
我觉得 C++ 标准进化最大的版本是 C++11,当然其经历的时间跨度也最大,之后的只是增补一些标准,使之更方 ...

我的人生经验就是:能不升级就不升级!
就像地球的地转轴的角度发生了改变的话,地球的气候又将发生变化,
人一旦习惯了过去的版本,再去习惯新版本很啰嗦,
我以前不明白老同志用的CAD为什么那么落后,
后来阅历增加,渐渐明白了,适应新的版本需要一个过程,
还可能面临着种种插件不兼容的问题,而人已经习惯了旧版本,
所以能稳定地干活才是硬道理!
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-3-18 12:46:08 | 显示全部楼层
像vim这种软件,升级后,有时候特性就变化了,
配置文件都改变了
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-3-18 12:48:06 | 显示全部楼层
我现在写脚本,都注明是什么操作系统,64位还是32位,运行的软件是什么版本,等等,
就怕升级后找不到原来的版本。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-11-22 04:10 , Processed in 0.031181 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表