std::labs 跨多个平台的未定义行为和无符号数
undefined behavior with std::labs with unsigned number across multiple platforms
我想求(a-b)的绝对值,其中a、b是32位无符号整数。我使用了 std::labs 如下所示。但是操作在不同的平台表现不同!
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
uint32_t x = 0, y = 0, z_u = 0, result_labs = 0, result_abs = 0, result_llabs = 0;
int32_t z = 0;
for (size_t i = 0; i < 10; i++)
{
x = rand();
y = rand();
z = x - y;
z_u = x - y;
result_labs = labs(x - y); //Problematic Call
result_abs = std::abs(static_cast<int32_t>(x) - static_cast<int32_t>(y));
result_llabs = static_cast<uint32_t>(llabs(x - y));
if ((result_abs != result_labs) || (result_abs != result_llabs))
{
printf("[Error] X: %d Y: %d z: %d z_u: %u \tlabs: %d - abs: %d llabs: %d\n", x, y, z, z_u, result_labs, result_abs, result_llabs);
}
}
return 0;
}
问题:
使用 std::labs 对无符号整数的操作在不同的平台上产生不同的结果。例如 gcc linux , ghs 平台
如何正确处理这个abs差值计算?
/*Windows PC
中 VS 中的示例输出
[Error] X: 41 Y: 18467 z: -18426 z_u: 4294948870 labs: 18426 - abs: 18426 llabs: -18426
[Error] X: 6334 Y: 26500 z: -20166 z_u: 4294947130 labs: 20166 - abs: 20166 llabs: -20166
[Error] X: 11478 Y: 29358 z: -17880 z_u: 4294949416 labs: 17880 - abs: 17880 llabs: -17880
[Error] X: 5705 Y: 28145 z: -22440 z_u: 4294944856 labs: 22440 - abs: 22440 llabs: -22440
[Error] X: 2995 Y: 11942 z: -8947 z_u: 4294958349 labs: 8947 - abs: 8947 llabs: -8947
[Error] X: 4827 Y: 5436 z: -609 z_u: 4294966687 labs: 609 - abs: 609 llabs: -609
GHS 输出
[Error] X: 11188 Y: 27640 z: -16452 z_u: 4294950844 labs: -16452 - abs: 16452 llabs: -16452
[Error] X: 4295 Y: 12490 z: -8195 z_u: 4294959101 labs: -8195 - abs: 8195 llabs: -8195
[Error] X: 5062 Y: 27943 z: -22881 z_u: 4294944415 labs: -22881 - abs: 22881 llabs: -22881
[Error] X: 21352 Y: 32044 z: -10692 z_u: 4294956604 labs: -10692 - abs: 10692 llabs: -10692
[Error] X: 4714 Y: 9737 z: -5023 z_u: 4294962273 labs: -5023 - abs: 5023 llabs: -5023
[Error] X: 17346 Y: 28482 z: -11136 z_u: 4294956160 labs: -11136 - abs: 11136 llabs: -11136
行为已定义(除了 printf
)。
您使用 x - y
参数调用函数。 x
和y
都是uint32_t
所以结果也是uint32_t
所以永远不会是负数。无符号类型的算术运算“环绕”。
labs
采用 long
参数,因此在传递给函数之前,参数被 转换 为 long
。所以 uint32_t
是 converted 到 long
,这是实现定义的,但基本上意味着大于 LONG_MAX
的值会导致负值。
您的 abs
是一个以 uint32_t
类型调用的模板,因为参数具有 uint32_t
类型。 uint32_t
将 永远不会 为负数,因此 (val >= static_cast<T>(0))
始终为真,并且它是一个恒等函数。
llabs
采用 long long
参数,因此该参数被转换为 long long
。 long long
至少有 64 位,LLONG_MAX
至少在 2^63-1
左右。 uint32_t
类型的任何值都可以用 long long
表示。 uint32_t
永远不会是负数,转换为 long long
不会改变值,所以 llabs
只是收到一个正值,所以 llabs
它什么都不做, returns原始值。
您的 printf
调用可能无效 - %u
用于打印 unsigned int
,而不是 uint32_t
。使用 inttypes.h
中的 PRIu32
,或使用 C++:
#include <cinttypes>
int main() {
uint32_t val;
printf("%"PRIu32"\n", val);
// or, for example explicit C-style cast:
printf("%u\n", (unsigned)val);
}
What is the correct way to implement the std::labs in c++?
long labs(long x) {
return x < 0 ? -x : x;
}
就够了。请注意,类型 显式 长。
我想求(a-b)的绝对值,其中a、b是32位无符号整数。我使用了 std::labs 如下所示。但是操作在不同的平台表现不同!
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
uint32_t x = 0, y = 0, z_u = 0, result_labs = 0, result_abs = 0, result_llabs = 0;
int32_t z = 0;
for (size_t i = 0; i < 10; i++)
{
x = rand();
y = rand();
z = x - y;
z_u = x - y;
result_labs = labs(x - y); //Problematic Call
result_abs = std::abs(static_cast<int32_t>(x) - static_cast<int32_t>(y));
result_llabs = static_cast<uint32_t>(llabs(x - y));
if ((result_abs != result_labs) || (result_abs != result_llabs))
{
printf("[Error] X: %d Y: %d z: %d z_u: %u \tlabs: %d - abs: %d llabs: %d\n", x, y, z, z_u, result_labs, result_abs, result_llabs);
}
}
return 0;
}
问题:
使用 std::labs 对无符号整数的操作在不同的平台上产生不同的结果。例如 gcc linux , ghs 平台 如何正确处理这个abs差值计算?
/*Windows PC
中 VS 中的示例输出[Error] X: 41 Y: 18467 z: -18426 z_u: 4294948870 labs: 18426 - abs: 18426 llabs: -18426
[Error] X: 6334 Y: 26500 z: -20166 z_u: 4294947130 labs: 20166 - abs: 20166 llabs: -20166
[Error] X: 11478 Y: 29358 z: -17880 z_u: 4294949416 labs: 17880 - abs: 17880 llabs: -17880
[Error] X: 5705 Y: 28145 z: -22440 z_u: 4294944856 labs: 22440 - abs: 22440 llabs: -22440
[Error] X: 2995 Y: 11942 z: -8947 z_u: 4294958349 labs: 8947 - abs: 8947 llabs: -8947
[Error] X: 4827 Y: 5436 z: -609 z_u: 4294966687 labs: 609 - abs: 609 llabs: -609
GHS 输出
[Error] X: 11188 Y: 27640 z: -16452 z_u: 4294950844 labs: -16452 - abs: 16452 llabs: -16452
[Error] X: 4295 Y: 12490 z: -8195 z_u: 4294959101 labs: -8195 - abs: 8195 llabs: -8195
[Error] X: 5062 Y: 27943 z: -22881 z_u: 4294944415 labs: -22881 - abs: 22881 llabs: -22881
[Error] X: 21352 Y: 32044 z: -10692 z_u: 4294956604 labs: -10692 - abs: 10692 llabs: -10692
[Error] X: 4714 Y: 9737 z: -5023 z_u: 4294962273 labs: -5023 - abs: 5023 llabs: -5023
[Error] X: 17346 Y: 28482 z: -11136 z_u: 4294956160 labs: -11136 - abs: 11136 llabs: -11136
行为已定义(除了 printf
)。
您使用 x - y
参数调用函数。 x
和y
都是uint32_t
所以结果也是uint32_t
所以永远不会是负数。无符号类型的算术运算“环绕”。
labs
采用 long
参数,因此在传递给函数之前,参数被 转换 为 long
。所以 uint32_t
是 converted 到 long
,这是实现定义的,但基本上意味着大于 LONG_MAX
的值会导致负值。
您的 abs
是一个以 uint32_t
类型调用的模板,因为参数具有 uint32_t
类型。 uint32_t
将 永远不会 为负数,因此 (val >= static_cast<T>(0))
始终为真,并且它是一个恒等函数。
llabs
采用 long long
参数,因此该参数被转换为 long long
。 long long
至少有 64 位,LLONG_MAX
至少在 2^63-1
左右。 uint32_t
类型的任何值都可以用 long long
表示。 uint32_t
永远不会是负数,转换为 long long
不会改变值,所以 llabs
只是收到一个正值,所以 llabs
它什么都不做, returns原始值。
您的 printf
调用可能无效 - %u
用于打印 unsigned int
,而不是 uint32_t
。使用 inttypes.h
中的 PRIu32
,或使用 C++:
#include <cinttypes>
int main() {
uint32_t val;
printf("%"PRIu32"\n", val);
// or, for example explicit C-style cast:
printf("%u\n", (unsigned)val);
}
What is the correct way to implement the std::labs in c++?
long labs(long x) {
return x < 0 ? -x : x;
}
就够了。请注意,类型 显式 长。