Блог пользователя Ignatyev_Aleksey

Автор Ignatyev_Aleksey, 13 лет назад, По-русски

Недавно обнаружил, что в C++ при умножении или делении нуля на отрицательное число, в ответ записывается не 0, а -0. Из-за этой особенности долгое время не мог сдать задачу. Решил проблему так:

printf("%llf", ans == 0 ? fabs(ans) : ans);

Существует ли более красивое решение?
  • Проголосовать: нравится
  • +15
  • Проголосовать: не нравится

»
13 лет назад, # |
  Проголосовать: нравится -8 Проголосовать: не нравится

А почему бы не сделать просто printf(“%llf”, fabs(ans)); ? Вообще такое происходит, потому что все операции с действительными числами выполняются с некоторой погрешностью, что значит что умножение и деление это не в чистом виде умножение и деление, поэтому и 0 может быть как 0+EPS так и 0-EPS.

  • »
    »
    13 лет назад, # ^ |
      Проголосовать: нравится +6 Проголосовать: не нравится

    Понял почему бы не сделать так. А можно ссылку на задачу, на которой так происходит, ибо по идее не должно быть ошибки.

    • »
      »
      »
      13 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится -23 Проголосовать: не нравится

      -0 <> 0 — это Вы правильно подметили! Задача и тесты, вылавливающие неаакуратную работу с вещественными числами в этом направлении размещена здесь. Сразу отмечу, что все тесты корректны, ответы целочисленны, и, естественно, как вариант ответа число "-0" не содержат.

      • »
        »
        »
        »
        13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Эта Ваша задача решается с помощью перебора целочисленных делителей свободного члена.

        • »
          »
          »
          »
          »
          13 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

          Как вариант просто сдать — да, можно сдать перебором. :(

          Но она же решается школьным математическим методом решения квадратного уравнения (собственно на это она и расчитана, а не на полный перебор), если акуратно работать с вещественными числами.

          Неправильный ответ должен решателю напомнить, что в памяти компьютера все числа хранятся не в десятичной форме, а в двоичной, и поэтому важен порядок вычислений.

          • »
            »
            »
            »
            »
            »
            13 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится

            Я говорил не про полный перебор, а про перебор делителей свободного члена. Не знаю, как для Вас, а для меня это тоже школьный математический метод, был у нас на одном из дополнительных предметов физико-математического профиля ("Избранные вопросы математики").

          • »
            »
            »
            »
            »
            »
            13 лет назад, # ^ |
            Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

            Речь не идёт о специализированных классах, изучавших углубленно математику или программирование.

            Тут речь должна начаться с того, откуда взята задача — это задача с ГИА (аналог ЕГЭ в Украине) и рассчитана на обычных школьников, которые сдают задачу не по предмету "Программирование" или "Углубленный курс математики", а учащиеся всех школ, выбравшие на ГИА предмет "Информатика", т.е. спец.знаний по математике или программированию не нужно, а понятие о делителях свободного члена для большинства из них, к глубокому сожалению, ещё больше непонятнее, чем полный перебор.

            • »
              »
              »
              »
              »
              »
              »
              13 лет назад, # ^ |
              Rev. 2   Проголосовать: нравится +17 Проголосовать: не нравится

              А понятие о двоичном представлении числа с плавающей точкой, ИМХО, еще непонятнее, чем понятие о делителях свободного члена.
              UPD. Я неплохо помню про Ваше мнение о том, что наиболее правильное решение задачи — то, которое придумали Вы и именно Вы. Продолжать спор дальше у меня нет никакого желания.

  • »
    »
    13 лет назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится

    Ответы сами по себе могут быть отрицательными.

  • »
    »
    13 лет назад, # ^ |
      Проголосовать: нравится +8 Проголосовать: не нравится

    Вообще-то ноль на самом деле может иметь знак: http://en.wikipedia.org/wiki/Signed_zero

  • »
    »
    13 лет назад, # ^ |
      Проголосовать: нравится -8 Проголосовать: не нравится

    Почему минусуют этот пост? Да, я ошибся малость, но это была не основная суть коммента, которая вроде как верна.

    • »
      »
      »
      13 лет назад, # ^ |
        Проголосовать: нравится +11 Проголосовать: не нравится

      Такое происходит, потому что стандарт IEEE 754 поддерживает 2 нуля: +0 и -0, которые хоть и равны, выводятся по-разному (см. ссылку выше).
      Пример:

      printf("%lf\n", -1.0*0.0);
      

      Выведется -0, хотя умножение выполнено точно.

      • »
        »
        »
        »
        13 лет назад, # ^ |
          Проголосовать: нравится +9 Проголосовать: не нравится

        нет, минусуют именно потому что он ошибся, так как вот это мало кто знал, пока вы не сказали

      • »
        »
        »
        »
        13 лет назад, # ^ |
          Проголосовать: нравится -26 Проголосовать: не нравится

        Да нет там точного умножения. Число 0.0 это не чистый ноль, это может быть например 0.000000000000000000126418, то есть это очень маленькое число, близкое к 0 (обычно приближается к нулю с положительной стороны), поэтому и умножение его на отрицательное даёт очень маленькое отрицательное число, то есть -0, что на самом деле -0.0000000000000000293647 (то есть близкое к нулю слева).

        • »
          »
          »
          »
          »
          13 лет назад, # ^ |
            Проголосовать: нравится +3 Проголосовать: не нравится
          printf("%.20lf\n", -1.0*0.0);
          

          Сколько знаков не выводил, после точки видел только нули.
          А вообще зачем 0.0 делать не чистым нулём, когда есть чистый +0?

        • »
          »
          »
          »
          »
          13 лет назад, # ^ |
            Проголосовать: нравится +5 Проголосовать: не нравится

          хочется вам верить, ну сколько знаков не вывел, ничего нет после запятой, кроме нулей

        • »
          »
          »
          »
          »
          13 лет назад, # ^ |
            Проголосовать: нравится +1 Проголосовать: не нравится

          Если глянуть в устройство вещественного числа по стандарту IEEE-754, то там нет никаких причин, почему 0.0 и -1.0 не могут быть представлены абсолютно точно. В этом формате число разложено по целым степеням двойки (4, 2, 1, 1/2, 1/4 ...), именно поэтому не получится точно представить 1/3 или даже 0.1.

    • »
      »
      »
      13 лет назад, # ^ |
        Проголосовать: нравится +1 Проголосовать: не нравится

      плюсану вас, пусть вам будет проще =)

      • »
        »
        »
        »
        13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Нет, просто коль так сильно минусуют, хочется знать за что. Может я ошибся в основной — второй части коммента.

»
13 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится

А в задаче, что, посимвольный чекер? И, если не секрет, она не на е-олимпе?

»
13 лет назад, # |
  Проголосовать: нравится +21 Проголосовать: не нравится

Можно к ответу всегда прибавлять EPS.

»
13 лет назад, # |
  Проголосовать: нравится +32 Проголосовать: не нравится

Я в Петрозаводске перед выводом написал "x = x;", и у меня выводилось без минуса.

»
13 лет назад, # |
  Проголосовать: нравится +25 Проголосовать: не нравится

Кстати, на правах оффтопа на тему отрицательных нулей и С++. Мне что-то последнее время Code::Blocks часто стал выдавать такого рода картинку:  Что бы это значило?

»
13 лет назад, # |
  Проголосовать: нравится +10 Проголосовать: не нравится

Вот как раз сегодня наткнулся, в вики была ссылка http://habrahabr.ru/post/112953/ Очень познавательно, советую.

»
13 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится

в посте значится printf(“%llf”, ans == 0 ? fabs(ans) : ans); мне не нравится здесь == 0 для даблов, думаю лучше (fabs(ans) < eps) ? 0.0 : ans;