abs(无符号长)有意义吗?

时间:2021-04-08 07:00:09

I've come across this code, which incidentally my profiler reports as a bottleneck:

我遇到了这个代码,顺便说一下,我的profiler报告是一个瓶颈:

#include <stdlib.h>

unsigned long a, b;
// Calculate values for a and b
unsigned long c;

c = abs(a - b);

Does that line do anything more interesting that c = a - b;? Do either of the options invoke undefined or implementation-defined behaviour, and are there other potential gotchas? Note that the C <stdlib.h> is included, not <cstdlib>.

这条线是否更有趣,c = a - b ?是否可以调用未定义的或实现定义的行为,还有其他可能的问题吗?注意,C 包含,而不是 。h>

3 个解决方案

#1


14  

Not it doesn't make sense.

不是说不通。

If you want the difference, use

如果你想要区别,就用吧。

c = (a > b) ? a - b : b - a;

or

c = max(a, b) - min(a, b);

Unsigned if go below zero would wrap back (effect is similar to adding 2sizeof (unsigned long) * CHAR_BIT)

如果低于0,则会被取消签名(效果类似于添加2sizeof(未签名的long) * CHAR_BIT)

If you are looking for difference between two numbers, you can write a small template as below

如果您正在寻找两个数字之间的差异,您可以在下面编写一个小模板。

namespace MyUtils {
  template<typename T>
  T diff(const T&a, const T&b) {
    return (a > b) ? (a - b) : (b - a);
  }
}

Looking at the declaration of abs inherited from C (Because you included stdlib.h)

查看从C继承的abs的声明(因为您包含stdlib.h)

int       abs( int n );
long      abs( long n );
long long abs( long long n ); //    (since C++11)
//Defined in header <cinttypes>
std::intmax_t abs( std::intmax_t n ); //    (since C++11)

And abs in C++ (from cmath)

和abs在c++(来自cmath)

float       abs( float arg );
double      abs( double arg );
long double abs( long double arg );

If you notice, both the argument and return type of each function are signed. So if you pass an unsigned type to one of these function, implicit conversion unsigned T1 -> signed T2 -> unsigned T1 would take place (where T1 and T2 may be same and T1 is long in your case). When you convert an unsigned integral to signed integral, the behavior is implementation dependendent if it can not be represented in a signed type.

如果您注意到,每个函数的参数和返回类型都将被签名。因此,如果您将一个未签名的类型传递给其中一个函数,隐式转换(unsigned T1 ->)将会发生(T1和T2可能相同,T1在您的情况下很长)。当您将一个未签名的积分转换为已签名的积分时,如果不能用已签名的类型表示,则该行为将依赖于实现。

From 4.7 Integral conversions [conv.integral]

从4.7个积分转换[对流。积分]

  1. If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). — end note]
  2. 如果目标类型是未签名的,则产生的值是源整数的最小无符号整数(模2n,其中n是用于表示未签名类型的比特数)。[注意:在两个补充表示中,这个转换是概念上的,并且位模式没有变化(如果没有截断)。——结束注意)
  3. If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.
  4. 如果目标类型已签名,则该值在目标类型(和位字段宽度)中表示时不变;否则,值就是实现定义的。

#2


7  

I don't know whether you'd regard it as making sense, but abs() applied to an unsigned value can certainly return a value other than the one passed in. That's because abs() takes an int argument and returns an int value.

我不知道您是否认为它是有意义的,但是应用于无符号值的abs()肯定会返回一个已传入的值。这是因为abs()接受一个int参数并返回一个int值。

For example:

例如:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    unsigned u1 = 0x98765432;
    printf("u1 = 0x%.8X; abs(u1) = 0x%.8X\n", u1, abs(u1));
    unsigned long u2 = 0x9876543201234567UL;
    printf("u2 = 0x%.16lX; abs(u2) = 0x%.16lX\n", u2, labs(u2));
    return 0;
}

When compiled as C or C++ (using GCC 4.9.1 on Mac OS X 10.10.1 Yosemite), it produces:

当以C或c++(在Mac OS X 10.10.1 Yosemite上使用GCC 4.9.1)编译时,它产生:

u1 = 0x98765432; abs(u1) = 0x6789ABCE
u2 = 0x9876543201234567; abs(u2) = 0x6789ABCDFEDCBA99

If the high bit of the unsigned value is set, then the result of abs() is not the value that was passed to the function.

如果设置了未签名值的高位,那么abs()的结果不是传递给函数的值。

The subtraction is merely a distraction; if the result has the most significant bit set, the value returned from abs() will be different from the value passed to it.

减法只是分散注意力;如果结果具有最重要的位集,那么从abs()返回的值将与传递给它的值不同。


When you compile this code with C++ headers, instead of the C headers shown in the question, then it fails to compile with ambiguous call errors:

当你用c++头编译这个代码,而不是在问题中显示的C标题时,它就不能用模糊的调用错误来编译:

#include <cstdlib>
#include <iostream>
using namespace std;

int main(void)
{
    unsigned u1 = 0x98765432;
    cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(u1) << "\n";
    unsigned long u2 = 0x9876543201234567UL;
    cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n";
    return 0;
}

Compilation errors:

编译错误:

absuns2.cpp: In function ‘int main()’:
absuns2.cpp:8:72: error: call of overloaded ‘abs(unsigned int&)’ is ambiguous
     cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(u1) << "\n";
                                                                        ^
absuns2.cpp:8:72: note: candidates are:
In file included from /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:72:0,
                 from absuns2.cpp:1:
/usr/include/stdlib.h:129:6: note: int abs(int)
 int  abs(int) __pure2;
      ^
In file included from absuns2.cpp:1:0:
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:174:3: note: long long int std::abs(long long int)
   abs(long long __x) { return __builtin_llabs (__x); }
   ^
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:166:3: note: long int std::abs(long int)
   abs(long __i) { return __builtin_labs(__i); }
   ^
absuns2.cpp:10:72: error: call of overloaded ‘abs(long unsigned int&)’ is ambiguous
     cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n";
                                                                        ^
absuns2.cpp:10:72: note: candidates are:
In file included from /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:72:0,
                 from absuns2.cpp:1:
/usr/include/stdlib.h:129:6: note: int abs(int)
 int  abs(int) __pure2;
      ^
In file included from absuns2.cpp:1:0:
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:174:3: note: long long int std::abs(long long int)
   abs(long long __x) { return __builtin_llabs (__x); }
   ^
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:166:3: note: long int std::abs(long int)
   abs(long __i) { return __builtin_labs(__i); }
   ^

So, the code in the question only compiles when only the C-style headers are used; it doesn't compile when the C++ headers are used. If you add <stdlib.h> as well as <cstdlib>, there's an additional overload available to make the calls more ambiguous.

因此,只有在使用c样式的头时,问题中的代码才会编译;当使用c++头时,它不会编译。如果您添加< stdlib。h>和 ,还有一个额外的重载可以使调用更加模糊。

You can make the code compile if you add (in)appropriate casts to the calls to abs(), and the absolute value of a signed quantity can be different from the original signed quantity, which is hardly surprising news:

如果您在对abs()的调用中添加(in)适当的强制转换,您可以使代码编译,并且一个签名数量的绝对值可能与原始签名的数量不同,这并不令人惊讶:

#include <cstdlib>
#include <iostream>
using namespace std;

int main(void)
{
    unsigned u1 = 0x98765432;
    cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(static_cast<int>(u1)) << "\n";
    unsigned long u2 = 0x9876543201234567UL;
    cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(static_cast<long>(u2)) << "\n";
    return 0;
}

Output:

输出:

u1 = 0x98765432; abs(u1) = 0x6789abce
u2 = 0x9876543201234567; abs(u2) = 0x6789abcdfedcba99

Moral: Don't use the C headers for which there are C++ equivalents in C++ code; use the C++ headers instead.

寓意:不要在c++代码中使用c++等价物的C头文件;使用c++头文件。

#3


0  

I think when c=a-b is negative, if c is unsigned number, c is not the accurate answer. Using abs to guarantee c is a positive number.

我认为当c=a-b是负数时,如果c是无符号数,c不是正确答案。用abs来保证c是一个正数。

#1


14  

Not it doesn't make sense.

不是说不通。

If you want the difference, use

如果你想要区别,就用吧。

c = (a > b) ? a - b : b - a;

or

c = max(a, b) - min(a, b);

Unsigned if go below zero would wrap back (effect is similar to adding 2sizeof (unsigned long) * CHAR_BIT)

如果低于0,则会被取消签名(效果类似于添加2sizeof(未签名的long) * CHAR_BIT)

If you are looking for difference between two numbers, you can write a small template as below

如果您正在寻找两个数字之间的差异,您可以在下面编写一个小模板。

namespace MyUtils {
  template<typename T>
  T diff(const T&a, const T&b) {
    return (a > b) ? (a - b) : (b - a);
  }
}

Looking at the declaration of abs inherited from C (Because you included stdlib.h)

查看从C继承的abs的声明(因为您包含stdlib.h)

int       abs( int n );
long      abs( long n );
long long abs( long long n ); //    (since C++11)
//Defined in header <cinttypes>
std::intmax_t abs( std::intmax_t n ); //    (since C++11)

And abs in C++ (from cmath)

和abs在c++(来自cmath)

float       abs( float arg );
double      abs( double arg );
long double abs( long double arg );

If you notice, both the argument and return type of each function are signed. So if you pass an unsigned type to one of these function, implicit conversion unsigned T1 -> signed T2 -> unsigned T1 would take place (where T1 and T2 may be same and T1 is long in your case). When you convert an unsigned integral to signed integral, the behavior is implementation dependendent if it can not be represented in a signed type.

如果您注意到,每个函数的参数和返回类型都将被签名。因此,如果您将一个未签名的类型传递给其中一个函数,隐式转换(unsigned T1 ->)将会发生(T1和T2可能相同,T1在您的情况下很长)。当您将一个未签名的积分转换为已签名的积分时,如果不能用已签名的类型表示,则该行为将依赖于实现。

From 4.7 Integral conversions [conv.integral]

从4.7个积分转换[对流。积分]

  1. If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). — end note]
  2. 如果目标类型是未签名的,则产生的值是源整数的最小无符号整数(模2n,其中n是用于表示未签名类型的比特数)。[注意:在两个补充表示中,这个转换是概念上的,并且位模式没有变化(如果没有截断)。——结束注意)
  3. If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.
  4. 如果目标类型已签名,则该值在目标类型(和位字段宽度)中表示时不变;否则,值就是实现定义的。

#2


7  

I don't know whether you'd regard it as making sense, but abs() applied to an unsigned value can certainly return a value other than the one passed in. That's because abs() takes an int argument and returns an int value.

我不知道您是否认为它是有意义的,但是应用于无符号值的abs()肯定会返回一个已传入的值。这是因为abs()接受一个int参数并返回一个int值。

For example:

例如:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    unsigned u1 = 0x98765432;
    printf("u1 = 0x%.8X; abs(u1) = 0x%.8X\n", u1, abs(u1));
    unsigned long u2 = 0x9876543201234567UL;
    printf("u2 = 0x%.16lX; abs(u2) = 0x%.16lX\n", u2, labs(u2));
    return 0;
}

When compiled as C or C++ (using GCC 4.9.1 on Mac OS X 10.10.1 Yosemite), it produces:

当以C或c++(在Mac OS X 10.10.1 Yosemite上使用GCC 4.9.1)编译时,它产生:

u1 = 0x98765432; abs(u1) = 0x6789ABCE
u2 = 0x9876543201234567; abs(u2) = 0x6789ABCDFEDCBA99

If the high bit of the unsigned value is set, then the result of abs() is not the value that was passed to the function.

如果设置了未签名值的高位,那么abs()的结果不是传递给函数的值。

The subtraction is merely a distraction; if the result has the most significant bit set, the value returned from abs() will be different from the value passed to it.

减法只是分散注意力;如果结果具有最重要的位集,那么从abs()返回的值将与传递给它的值不同。


When you compile this code with C++ headers, instead of the C headers shown in the question, then it fails to compile with ambiguous call errors:

当你用c++头编译这个代码,而不是在问题中显示的C标题时,它就不能用模糊的调用错误来编译:

#include <cstdlib>
#include <iostream>
using namespace std;

int main(void)
{
    unsigned u1 = 0x98765432;
    cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(u1) << "\n";
    unsigned long u2 = 0x9876543201234567UL;
    cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n";
    return 0;
}

Compilation errors:

编译错误:

absuns2.cpp: In function ‘int main()’:
absuns2.cpp:8:72: error: call of overloaded ‘abs(unsigned int&)’ is ambiguous
     cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(u1) << "\n";
                                                                        ^
absuns2.cpp:8:72: note: candidates are:
In file included from /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:72:0,
                 from absuns2.cpp:1:
/usr/include/stdlib.h:129:6: note: int abs(int)
 int  abs(int) __pure2;
      ^
In file included from absuns2.cpp:1:0:
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:174:3: note: long long int std::abs(long long int)
   abs(long long __x) { return __builtin_llabs (__x); }
   ^
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:166:3: note: long int std::abs(long int)
   abs(long __i) { return __builtin_labs(__i); }
   ^
absuns2.cpp:10:72: error: call of overloaded ‘abs(long unsigned int&)’ is ambiguous
     cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n";
                                                                        ^
absuns2.cpp:10:72: note: candidates are:
In file included from /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:72:0,
                 from absuns2.cpp:1:
/usr/include/stdlib.h:129:6: note: int abs(int)
 int  abs(int) __pure2;
      ^
In file included from absuns2.cpp:1:0:
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:174:3: note: long long int std::abs(long long int)
   abs(long long __x) { return __builtin_llabs (__x); }
   ^
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:166:3: note: long int std::abs(long int)
   abs(long __i) { return __builtin_labs(__i); }
   ^

So, the code in the question only compiles when only the C-style headers are used; it doesn't compile when the C++ headers are used. If you add <stdlib.h> as well as <cstdlib>, there's an additional overload available to make the calls more ambiguous.

因此,只有在使用c样式的头时,问题中的代码才会编译;当使用c++头时,它不会编译。如果您添加< stdlib。h>和 ,还有一个额外的重载可以使调用更加模糊。

You can make the code compile if you add (in)appropriate casts to the calls to abs(), and the absolute value of a signed quantity can be different from the original signed quantity, which is hardly surprising news:

如果您在对abs()的调用中添加(in)适当的强制转换,您可以使代码编译,并且一个签名数量的绝对值可能与原始签名的数量不同,这并不令人惊讶:

#include <cstdlib>
#include <iostream>
using namespace std;

int main(void)
{
    unsigned u1 = 0x98765432;
    cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(static_cast<int>(u1)) << "\n";
    unsigned long u2 = 0x9876543201234567UL;
    cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(static_cast<long>(u2)) << "\n";
    return 0;
}

Output:

输出:

u1 = 0x98765432; abs(u1) = 0x6789abce
u2 = 0x9876543201234567; abs(u2) = 0x6789abcdfedcba99

Moral: Don't use the C headers for which there are C++ equivalents in C++ code; use the C++ headers instead.

寓意:不要在c++代码中使用c++等价物的C头文件;使用c++头文件。

#3


0  

I think when c=a-b is negative, if c is unsigned number, c is not the accurate answer. Using abs to guarantee c is a positive number.

我认为当c=a-b是负数时,如果c是无符号数,c不是正确答案。用abs来保证c是一个正数。