Имеет ли смысл abs (unsigned long) смысл?
Я столкнулся с этим кодом, который, кстати, мой профилировщик описывает как узкое место:
#include <stdlib.h>
unsigned long a, b;
// Calculate values for a and b
unsigned long c;
c = abs(a - b);
Эта строка делает что-то интересное, что c = a - b;
? Использовать ли какой-либо из параметров undefined или поведение, определяемое реализацией, и есть ли другие потенциальные ошибки? Обратите внимание, что включен C <stdlib.h>
, а не <cstdlib>
.
Ответы
Ответ 1
Нет, это не имеет смысла.
Если вы хотите разницу, используйте
c = (a > b) ? a - b : b - a;
или
c = max(a, b) - min(a, b);
Без знака, если значение ниже нуля, будет выполнено обратное преобразование (эффект аналогичен добавлению 2 sizeof (unsigned long) * CHAR_BIT)
Если вы ищете разницу между двумя числами, вы можете написать небольшой шаблон, как показано ниже
namespace MyUtils {
template<typename T>
T diff(const T&a, const T&b) {
return (a > b) ? (a - b) : (b - a);
}
}
Рассматривая декларацию abs, унаследованную от C
(потому что вы включили 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)
и abs в C++
(из cmath
)
float abs( float arg );
double abs( double arg );
long double abs( long double arg );
Если вы заметили, как аргумент, так и возвращаемый тип каждой функции signed
. Поэтому, если вы передадите тип без знака одной из этих функций, произойдет неявное преобразование unsigned T1 -> signed T2 -> unsigned T1
(где T1
и T2
могут быть одинаковыми, а T1
в вашем случае - long
). Когда вы конвертируете неподписанный интеграл в подписанный интеграл, поведение зависит от реализации, если его нельзя представить в подписанном типе.
Из 4.7 Интегральных преобразований [conv.integral]
-
Если тип назначения не имеет знака, полученное значение является наименьшим целое число без знака, совпадающее с целым числом источника (по модулю 2 n, где n количество битов, используемых для представления типа без знака). [Примечание: в дополняют представление, это преобразование является концептуальным и нет изменений в битовой комбинации (если нет усечения). - конечная нота]
- Если тип назначения подписан, значение не изменяется, если оно может быть представлены в типе назначения (и ширине битового поля); в противном случае, значение определяется реализацией.
Ответ 2
Я не знаю, считаете ли вы его смысл, но abs()
, примененный к значению без знака, может, конечно, вернуть значение, отличное от того, которое было передано. Это потому, что abs()
принимает аргумент int
и возвращает значение int
.
Например:
#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;
}
При компиляции C или С++ (с использованием GCC 4.9.1 в Mac OS X 10.10.1 Yosemite), он производит:
u1 = 0x98765432; abs(u1) = 0x6789ABCE
u2 = 0x9876543201234567; abs(u2) = 0x6789ABCDFEDCBA99
Если высокий бит значения без знака установлен, тогда результат abs()
не является значением, переданным функции.
Вычитание - это просто отвлечение; если результат имеет самый старший бит, значение, возвращаемое из abs()
, будет отличаться от значения, переданного ему.
Когда вы компилируете этот код с заголовками С++, вместо заголовков 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;
}
Ошибки компиляции:
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); }
^
Итак, код в вопросе компилируется только тогда, когда используются только заголовки C-стиля; он не компилируется, когда используются заголовки С++. Если вы добавите <stdlib.h>
, а также <cstdlib>
, добавлена дополнительная перегрузка, чтобы сделать вызовы более неоднозначными.
Вы можете сделать компиляцию кода, если вы добавите (в) соответствующие приведения к вызовам abs()
, а абсолютное значение подписанной суммы может отличаться от оригинальной подписанной величины, что вряд ли удивительно новость:
#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;
}
Вывод:
u1 = 0x98765432; abs(u1) = 0x6789ABCE
u2 = 0x9876543201234567; abs(u2) = 0x6789ABCDFEDCBA99
Мораль: не используйте заголовки C, для которых существуют С++-эквиваленты в коде на С++; вместо этого используйте заголовки С++.
Ответ 3
Я думаю, что когда c = a-b отрицательно, если c - непознанное число, c не является точным ответом. Использование abs для обеспечения c является положительным числом.