Поведение C ++ pow с расчетом отрицательного размера вектора - PullRequest
1 голос
/ 20 октября 2019

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

#include <iostream>
#include <vector>
#include <math.h>

using namespace std;

int main() {
    vector<vector<int>> G = {
        {1, 2, 3}, 
        {0, 4}
    };
    cout << pow(G[1].size() - G[0].size(), 2) << endl;
    return 0;
}

Это печатает 1.84467e + 019.

Ответы [ 3 ]

3 голосов
/ 20 октября 2019

Тип .size() равен unsigned, и вы не можете просто вычесть их, когда левый операнд меньше правого.

Попробуйте это:

cout << pow((long) G[1].size() - (long)G[0].size(), 2) << endl;
            ~~~~~~               ~~~~~~

Однакоэто решение основано на предположении, что приведение результата .size() вписывается в signed long.


Если вы хотите более защитный код, попробуйте этот:

size_t size_diff(size_t s0, size_t s1)
{
    return s0 < s1? (s1 - s0) : (s0 - s1);
}

int main() {
    // ...   
    cout << pow(size_diff(G[1].size(), G[0].size()), 2) << endl;
} 
2 голосов
/ 20 октября 2019

В дополнение к принятому ответу я хотел бы отметить, что в C ++ 20 у нас будет std::ssize() свободная функция, которая возвращает размер как значение типа со знаком. Тогда

std::pow(std::ssize(G[1]) - std::ssize(G[0]), 2)

даст правильный результат без явного приведения типов.

0 голосов
/ 21 октября 2019

Поскольку pow принимает значение с плавающей запятой в качестве первого аргумента, я бы предложил компилятору выбрать правильное продвижение, добавив разницу к 0.0 (или 0.0L) :

#include <iostream>
#include <cstdint>
#include <cmath>

using namespace std;

int main()
{
  std::string name;
  /// 52 of 64 bits used
  uint64_t n1 = 0x000ffffffffffffd;
  uint64_t n2 = 0x000fffffffffffff;

  cout << "plain: " << n1 - n2 << endl;
  cout << "float: " << (float)n1 - (float)n2 << endl;
  cout << "double: " << (double)n1 - (double)n2 << endl;
  cout << "long double: " << (long double)n1 - (long double)n2 << endl;
  cout << "0.0+: " << 0.0 + n1 - n2 << endl;
  cout << "0.0L+: " << 0.0L + n1 - n2 << endl;
  cout << "pow(plain, 2): " << pow(n1-n2, 2) << endl;
  cout << "pow(0.0+diff, 2): " << pow(0.0+n1-n2, 2) << endl;
  cout << "pow(0.0L+diff, 2): " << pow(0.0L+n1-n2, 2) << endl;
}

Вывод

plain: 18446744073709551614
float: 0
double: -2
long double: -2
0.0+: -2
0.0L+: -2
pow(plain, 2): 3.40282e+38
pow(0.0+diff, 2): 4
pow(0.0L+diff, 2): 4

показывает, что обычное вычитание идет не так. Даже приведение к float не достаточно, потому что float предоставляет только 23-битную мантиссу.

Решение о том, использовать ли 0.0 или 0.0L для различий size_t значений, возвращаемых реальнымstd::vector::size() вызовов является теоретическим для процессов с адресным пространством ниже 4,5 петабайт.

Поэтому я думаю, что подойдет следующее:

cout << pow(0.0 + G[1].size() - G[0].size(), 2) << endl;
...