vak: (Знайка)
[personal profile] vak
На собеседованиях я часто спрашиваю программистов, как сложить на Си два целых числа. Проверим, как с этим справится Гугл Бард.
Please create a function "int add(int a, int b)", which adds two integer numbers and avoids undefined behavior.
Мгновенный ответ:
Sure, here is the C function int add(int a, int b) that adds two integer numbers and avoids undefined behavior:
int add(int a, int b) {
  int sum;

  /* Check for overflow */
  if (a > 0 && b > 0 && (a > INT_MAX - b)) {
    /* Overflow detected */
    return INT_MAX;
  } else if (a < 0 && b < 0 && (a < INT_MIN - b)) {
    /* Overflow detected */
    return INT_MIN;
  }

  /* Add the two numbers */
  sum = a + b;

  return sum;
}
This function first checks for overflow by comparing the two numbers to the maximum and minimum values for an integer. If either number is greater than the maximum value or less than the minimum value, then the function returns INT_MAX or INT_MIN, respectively. Otherwise, the two numbers are added together and the result is returned.

This function avoids undefined behavior by checking for overflow before adding the two numbers. If overflow is detected, the function returns a safe value instead of crashing or producing unexpected results.
За последние несколько лет ни от кого из сапиенсов я не получал настолько быстрый и исчерпывающий ответ. Но продолжим:
Please create a few tests for this function.
Пауза чуть подольше:
Sure, here are a few tests for the add() function:
#include <stdio.h>

int add(int a, int b);

int main() {
  int expected, actual;

  /* Positive numbers */
  expected = 10;
  actual = add(5, 5);
  if (expected != actual) {
    printf("add(5, 5) expected %d, got %d\n", expected, actual);
    return 1;
  }

  /* Negative numbers */
  expected = -10;
  actual = add(-5, -5);
  if (expected != actual) {
    printf("add(-5, -5) expected %d, got %d\n", expected, actual);
    return 1;
  }

  /* Overflow */
  expected = INT_MAX;
  actual = add(INT_MAX - 1, 1);
  if (expected != actual) {
    printf("add(INT_MAX - 1, 1) expected %d, got %d\n", expected, actual);
    return 1;
  }

  /* Underflow */
  expected = INT_MIN;
  actual = add(INT_MIN + 1, -1);
  if (expected != actual) {
    printf("add(INT_MIN + 1, -1) expected %d, got %d\n", expected, actual);
    return 1;
  }

  return 0;
}
These tests cover a variety of cases, including positive numbers, negative numbers, overflow, and underflow. If all of these tests pass, then it is likely that the add() function is working correctly.
Неплохо, но я же дотошный, продолжаю усугублять.
Please use Googletest for tests.
Этого Барда ничем не проймёшь.
Sure, here are the same tests written using GoogleTest:
#include <gtest/gtest.h>

int add(int a, int b);

TEST(AddTest, Positive) {
  EXPECT_EQ(10, add(5, 5));
}

TEST(AddTest, Negative) {
  EXPECT_EQ(-10, add(-5, -5));
}

TEST(AddTest, Overflow) {
  EXPECT_EQ(INT_MAX, add(INT_MAX - 1, 1));
}

TEST(AddTest, Underflow) {
  EXPECT_EQ(INT_MIN, add(INT_MIN + 1, -1));
}
These tests are written using the GoogleTest framework, which provides a more structured and organized way to write tests. The EXPECT_EQ() macro asserts that the two arguments are equal. If they are not equal, the test will fail.

These tests can be run using the gtest command-line tool. For example, to run all of the tests, you would use the following command:
gtest add_test.cc
If all of the tests pass, then the output will be:
[==========] Running 4 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 4 tests from AddTest
[ RUN      ] AddTest.Positive
[       OK ] AddTest.Positive (0 ms)
[ RUN      ] AddTest.Negative
[       OK ] AddTest.Negative (0 ms)
[ RUN      ] AddTest.Overflow
[       OK ] AddTest.Overflow (0 ms)
[ RUN      ] AddTest.Underflow
[       OK ] AddTest.Underflow (0 ms)
[----------] 4 tests from AddTest (0 ms total)

[----------] No test cases ran.
[----------] No assertions have been violated.

[==========] 4 tests passed.

Ну что сказать. Искусственный интеллект становится реальным помощником программисту.

Date: 2023-04-25 21:47 (UTC)
From: [personal profile] dijifi
Acceptable undefined behavior may be “the processor turns into a lizard and runs away”

Date: 2023-04-25 22:12 (UTC)
fizzik: (Default)
From: [personal profile] fizzik
Я имею в виду, что вместо того, чтобы вовзращать из функции сложения выделенное значение, сигнализирующее о переполнении, можно бросить исключение, сигнализирующее о том же. Очевидное преимущество в том, что в длинной цепчке вычислений нам не надо тогда проверять после каждого шага, не случилось ли переполнения. Ведь если у нас 50 шагов вычислений, то в большинстве случаев не важно, получили ли мы переполнение уже на первом шаге, или на 50-м. Важно только, что корректного результата у нас нет. Код, который после каждого из 50 шагов будет проверять результат на выделенные значения чтобы свалиться в ветку обработки ошибки, окажется абсолютно нечитаем. Вместо этого мы можем писать наши вычисления как бы не задумываясь о переполнениях (однако используя не стандартные арифметические операции, а функции вроде написанной ботом, но бросающие исключения), а ветку обработки переполнения выполнять, поймав исключение там, где это удобно.