2013-04-19 5 views
7

Я получаю предупреждение за сопоставленное сравнение без знака, когда сравниваю std::abs(int) с unsigned. И действительно, std::abs возвращает подписанные значения. Почему этот выбор был сделан? Он решил бы проблему отрицательных значений, абсолютная величина которых не может быть представлена ​​в подписанном типе.Почему std :: abs возвращает подписанные типы

И тогда, есть ли что-то более чистое (например, без приведения), чем это, чтобы избежать предупреждений?

#include <cassert> 
#include <cstdlib> 

// max(1, lhs + rhs). (lhs must be > 0) 
unsigned add(unsigned lhs, int rhs) 
{ 
    return 
    (0 < rhs || static_cast<unsigned>(-rhs) < lhs 
    ? rhs + lhs 
    : 1); 
} 

int main() 
{ 
    assert(add(42, -41) == 1); 
    assert(add(42, 0) == 43); 
    assert(add(42, 1) == 43); 
    assert(add(42, -51) == 1); 
} 
+0

Я сомневаюсь, что это оказало большое влияние на стандартное обоснование, но на самом деле не гарантируется, что 'unsigned' может представлять каждое абсолютное значение' int'. Теоретически у вас может быть соответствующая реализация, в которой 'int' является дополнением 33 бит 2, а' unsigned' - 32 бита. В этой реализации ни 'int', ни' unsigned' не могут представлять абсолютное значение 'INT_MIN'. Не уверен, почему вы хотите такую ​​реализацию, конечно, но это законно, и поэтому ваше предложение не * строго * решает проблему. Стандарт по-прежнему допускает реализацию, где вы не можете выполнить 'abs (INT_MIN)'! –

+0

И на практике тот факт, что есть одно значение 'int', на которое вы не можете выполнить' abs', на самом деле не является более сложным, чем тот факт, что есть одно значение 'int', на которое вы не можете сделать '--'. Точно так же значение, на которое вы не можете выполнить '++'. Авторы C и C++ не слишком ненавидят неопределенное поведение переполнения, чтобы удалить его. –

+0

@SteveJessop Как это возможно, учитывая §3.9.1-3 (такое же количество хранения и одно и то же представление объекта для 'unsigned int' и' int')? –

ответ

6

Короткий ответ заключается в том, что это делается так, чтобы возвращаемый тип abs был таким же, как и его тип ввода. Это именно то, что вы хотите, большую часть времени.

В основном, когда вы вызываете abs, вы имеете дело с уравнением, где все элементы одного типа (или вы получите предупреждения), и вы хотите использовать величину некоторой переменной в этом уравнении. Это не означает, что вы хотите изменить тип одной из переменных в своем уравнении. Это даст некоторые проблемы/предупреждения, которые вы упоминаете.

Итак, короче говоря, более естественно и естественно использовать одинаковый тип ввода и вывода при запросе абсолютного значения подписанной переменной. Величина значения обычно не используется как индекс.

+0

+1 Абсолютно. В конце концов, это математическая функция и не должна испортить мне типа. Вы не хотите, чтобы 'std :: abs (1.0)' также возвращал 'unsigned double'. Точно так же' std :: floor' не возвращает 'int' (ok, это может также заботятся о представимых диапазонах в дополнение к простому концептуальному типу сохранения). –

+0

@ChristianRau: Я не знаю, что такое '' unsigned double', но я, конечно, не хочу никого * ;-) И некоторые люди примерно то же самое о неподписанных целых типах. –

+0

@ChristianRau Точно моя точка! Кроме того, я согласен с @SteveJessop, что 'unsigned double' звучит очень немотивированным, и я, конечно, не пропустил его в стандарте. – Agentlien

2

Это не выбор, то есть определение. abs реализуется через шаблон, который возвращает тип, в котором было сохранено исходное значение. Результат всегда будет действительным, поскольку абсолютное значение целого числа со знаком всегда будет соответствовать его исходному типу данных. Явное приведение к целевому типу данных должно быть достаточно, чтобы избавиться от любых предупреждений ...

+5

"абсолютное значение знакового целого всегда будет соответствовать в его исходном типе данных "- это не так. Почти во всех реализациях абсолютное значение 'INT_MIN' не вписывается в' int', а 'abs (INT_MIN)' вызывает переполнение. –

+1

'abs реализуется через шаблон'. [Abs] (http://en.cppreference.com/w/cpp/numeric/math/abs) Я знаю, что это не шаблон. –

+0

Это варианты абс, которые существуют в соответствии со стандартом: http://en.cppreference.com/w/cpp/numeric/math/abs Единственными из них, которые являются шаблонами, являются те, которые принимают типы шаблонов в качестве входных данных , (valarray и комплекс). – Agentlien

3

В C++ 11 вы можете написать свой собственный, чтобы бросить автоматический:

#include <utility> 

template< typename T > 
typename std::make_unsigned<T>::type abs(T x) 
{ 
    //We need to cast before negating x to avoid the overflow. 
    return x < 0? -static_cast<std::make_unsigned<T>::type>(x) : x; 
} 

Я пробовал с -Wall вариантов, не получил никаких предупреждений.

+1

Btw этот код по-прежнему имеет UB при передаче 'INT_MIN', он не« решает проблему отрицательных значений, абсолютное значение которых невозможно представить ». Но он сохраняет текст 'static_cast' каждый раз, когда вы его используете. –

+0

Я тоже так думал, но потом я попробовал: \t int x = std :: numeric_limits :: min(); \t unsigned y = my_abs (x); \t \t cout << x << endl; \t cout << y << endl; – fstamour

+0

Дело в том, что если у вас есть N бит целое число со знаком, равное -2^(N-1), вы не можете представить его абсолютное значение в том же формате (т. Е. N битов со знаком целого числа), поскольку наибольшее число, которое может быть представлено 2^(N-1) -1. Но std :: make_unsigned :: type дает 2^N unsigned, которые имеют максимум 2^N, который больше любого знакового значения с таким же количеством бит. – fstamour

 Смежные вопросы

  • Нет связанных вопросов^_^