Автор Тема: [Задачка-закачка] abs() - намиране на алтернатива  (Прочетена 5610 пъти)

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Няма проблем при препълване, поне на x86 архитектура. Причината е че единственият случай в който имаме препълване е когато събираме 0xFFFFFFFF (-1) с 1 (ред. :глупости). На x86 архитектура, резултатът се wrap-ва и накрая имаме правилен резултат, защото xor-ваме с 1. (ред. : глупости, виж по-долния пост).

Но на други архитектури съм съгласен, може да има проблеми.
« Последна редакция: Jul 18, 2011, 00:21 от gat3way »
Активен

"Knowledge is power" - France is Bacon

b2l

  • Напреднали
  • *****
  • Публикации: 4786
  • Distribution: MCC Interim
  • Window Manager: - // - // -
  • ...sometimes I feel like screaming... || RTFM!
    • Профил
    • WWW
Колеги кой ви е бил по главата да използвате такива сложни функции като корен квадратен, а не
използвате само булева функция AND с константа  :)

Ето кого:

- Да не се използва if (така че случая с if (a < 0) return a*-1 - отпада), switch и всякакви вариации на : ? (в PHP) && || и т.н.
- Да не се създават никакви променливи във функцията abs()
Активен

"Човекът е въже, опънато между звяра и свръхчовека, въже над пропаст. Човекът е нещо, което трябва да бъде превъзмогнато." - Фр. Ницше

b2l

  • Напреднали
  • *****
  • Публикации: 4786
  • Distribution: MCC Interim
  • Window Manager: - // - // -
  • ...sometimes I feel like screaming... || RTFM!
    • Профил
    • WWW
sqrt(a^2) !?
Компилирано под C++, при a=3, извежда 1...

Това как го получи  ????
Активен

"Човекът е въже, опънато между звяра и свръхчовека, въже над пропаст. Човекът е нещо, което трябва да бъде превъзмогнато." - Фр. Ницше

h_paskalev

  • Гост
sqrt(a^2) !?
Компилирано под C++, при a=3, извежда 1...

Това как го получи  ????
В C++ този израз наистина връща 1, въпреки че с някои компилатори се изисква преработка, за да бъде компилиран. Връща единица, защото:
a = 3, записано в двоична система е 11, a 2 записано в двоична система е 10. Логическото изключващо "или"(^) на 11 и 10 връща единица.
Корен от 1 връща 1, но както казах не съм адресирал някой конкретен език, а само израз като решение...
Записването, което съм представил е присъщо за системи като Mathematica и Maple.
« Последна редакция: Jul 18, 2011, 01:02 от H. Paskalev »
Активен

bot

  • Гост
 Аз пък си мислех че със символът ^ се изразява повдигане на степен, а логическият еквивалент на повдигане на квадрат е "ротация наляво". Между другото това е най-елементарния начин за намиране на абсолютна стойнот - корен квадратен от повдигнато на квадрат число.
Активен

bot

  • Гост
 говоря глупости, с ротация наляво се изразява умножено по 2. Съжалявам!
Активен

h_paskalev

  • Гост
Аз пък си мислех че със символът ^ се изразява повдигане на степен, а логическият еквивалент на повдигане на квадрат е "ротация наляво". Между другото това е най-елементарния начин за намиране на абсолютна стойнот - корен квадратен от повдигнато на квадрат число.

Както казах този символ се използва в някои системи за изразяване на повдигане на степен, но не и в повечето програмни езици(в частност C++).
Наистина това е най-елементарното решение, за което се сетих.
За побитовите операции може да се спретне доста дълга дискусия. Добре е да се покажат няколко решения, всяко си има плюсовете и минусите.
 [_]3 :)
Активен

bot

  • Гост
 Това е голям проблем за езиците от високо ниво - никой не знае колко такта (процесорно време) отнема една функция, всичко зависи от това как компилаторът ще "смели" дадена команда на машинен код за съответния процесор. На мен ми се е налагало често да превключвам на асемблер дори когато пиша на #C за да оптимизирам една операция и да изравня времената при изчисление.  Примерно при кода, който съм дал по-нагоре, ако търсим абсолютна стойност на положително число изчислението ще приключи по-рано, което не е желателно, т.е. една подпрограма трябва да има точно определено време, независимо от това колко време ще отнемат отделните резултати. Често се налага да се добавят "празни" команди (NOP инструкция) за да се изравнят времената. Това е необходимо, когато се използва watchdog функцията - знаем че една подпрограма отнема да речем 10mS, ако процесорът се забави по-дълго това означава че е "забил" и watchdog хардуерът не получава навреме kick инструкция, тогава процесорът се рестартира, но не искаме това да се случи само защото конкретния резултат изисква малко по-дълго време за изчисление.
Активен

h_paskalev

  • Гост
И това може да се превърне в огромна дискусия, а може би по-скоро поредната "кавга".
Език от ниско или високо ниво, "управляем" или не, интерпретиране или компилиране...
За всяко си има място под слънцето и привърженици.
Но съм съгласен, че забравата на начина, по който работи машината и нейния "майчин" език, определено е навредило доста.
Не може да се отрекат и облагите от съвременните методи на разработка, макар и често затъпяващи.
Активен

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Хм глупости съм говорил, нещата били далеч по-сложни. Препълването на signed int се оказва че има undefined поведение и по стечение на обстоятелствата, gcc все пак го wrap-ва, както прави с unsigned целочислените операции. Доколкото разбирам, това е дефолтното поведение в gcc4.x. При това положение все пак съм прав. Ще опростя нещата - нека int е 3-битов тип и да направим една truth таблица...

Първите 4 колони са signed int стойности, където побитовото отместване е аритметично и "запазва" sign bit-а. последната колона е резултатът когато кастнем това до unsigned int (понеже abs() по задание трябва да връща unsigned int стойност)


x          |   (x>>31)  |  x+(x>>31) |  (x+(x>>31))^(x>>31) | (unsigned int)result
========================================
0 (000) | 0(000)      |  0(000)       | 0(000)                          | 0
1 (001) | 0(000)      |  1(001)       | 1(001)                          | 1
2 (010) | 0(000)      |  2(010)       | 2(010)                          | 2
3 (011) | 0(000)      |  3(011)       | 3(011)                          | 3
-4(100) | -1(111)    |  3(011)       | -4(100)                         | 4
-3(101) | -1(111)    | -4(100)       | 3(011)                          | 3
-2(110) | -1(111)    | -3(101)       | 2(010)                          | 2
-1(111) | -1(111)    | -2(110)       | 1(001)                          | 1


Всичко това разчита на факта, че (поне gcc) поради някаква причина под "undefined behaviour" предпочита да wrap-ва резултата при препълване, по същият начин както с unsigned int препълването. При мен това е при всички -O опции. Обаче това също така е нещо, което компилатора приема за правилно, в резултат на което горната сметка работи. С друг компилатор, може и да не е така.
Активен

"Knowledge is power" - France is Bacon

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Йей, още нещо забавно (и доста очаквано апропо). Ако човек погледне моята "truth табличка", ще забележи, че abs(-4) връща -4 ако не се кастне до unsigned int, понеже 4 няма представяне в signed вид. Абсолютно същото важи и за abs() функцията която идва с libc:

Код
GeSHi (C):
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3.  
  4. void main()
  5. {
  6. unsigned int a=0x80000000;
  7. int b=a;
  8.  
  9. printf("abs(a)=%d\n",abs(a));
  10. printf("(unsigned int)abs(a)=%u\n",b);
  11.  
  12. }

....

Код
GeSHi (Bash):
  1. ./abs
  2. abs(a)=-2147483648
  3. (unsigned int)abs(a)=2147483648
  4.  

Това поведение е очаквано де, не мисля, че има нещо по-правилно в случая.

Така че умната при ползване на abs() ехех...в един определен случай може да има изненада :)

« Последна редакция: Jul 18, 2011, 15:26 от gat3way »
Активен

"Knowledge is power" - France is Bacon

VladSun

  • Напреднали
  • *****
  • Публикации: 2166
    • Профил
Код
GeSHi (PHP):
  1. <?php
  2.  
  3. function _abs($a)
  4. {
  5. return ($a >= 0) * $a + ($a < 0) * (- $a);
  6. }
  7.  
  8. assert("_abs(2) === 2");
  9. assert("_abs(1) === 1");
  10. assert("_abs(0) === 0");
  11. assert("_abs(-1) === 1");
  12. assert("_abs(-2) === 2");
  13.  

Решението е валидно за повечето популярни езици (може би при някои ще се наложи type cast към int на булевите изрази)
« Последна редакция: Jul 18, 2011, 11:37 от VladSun »
Активен

KISS Principle ( Keep-It-Short-and-Simple )
http://openfmi.net/projects/flattc/
Има 10 вида хора на този свят - разбиращи двоичния код и тези, които не го разбират :P

b2l

  • Напреднали
  • *****
  • Публикации: 4786
  • Distribution: MCC Interim
  • Window Manager: - // - // -
  • ...sometimes I feel like screaming... || RTFM!
    • Профил
    • WWW
sqrt(a^2) !?
Компилирано под C++, при a=3, извежда 1...

Това как го получи  ????
В C++ този израз наистина връща 1, въпреки че с някои компилатори се изисква преработка, за да бъде компилиран. Връща единица, защото:
a = 3, записано в двоична система е 11, a 2 записано в двоична система е 10. Логическото изключващо "или"(^) на 11 и 10 връща единица.
Корен от 1 връща 1, но както казах не съм адресирал някой конкретен език, а само израз като решение...
Записването, което съм представил е присъщо за системи като Mathematica и Maple.

Баси езика за писане на програми дето смята грешно!
Активен

"Човекът е въже, опънато между звяра и свръхчовека, въже над пропаст. Човекът е нещо, което трябва да бъде превъзмогнато." - Фр. Ницше

CTEHATA

  • Напреднали
  • *****
  • Публикации: 101
    • Профил
Код
GeSHi (SQL):
  1. mysql> USE test
  2. DATABASE changed
  3. mysql> delimiter |
  4. mysql> CREATE DEFINER = 'CTEHATA'@'%' FUNCTION `new_abs`(anInt INTEGER(11))
  5.    ->     RETURNS int(11)
  6.    ->     DETERMINISTIC
  7.    ->     CONTAINS SQL
  8.    ->     SQL SECURITY INVOKER
  9.    -> BEGIN
  10.    ->   RETURN anInt*SIGN(anInt) ;
  11.    -> END;
  12.    -> |    
  13. Query OK, 0 rows affected (0.04 sec)
  14.  
  15. mysql> delimiter ;
  16. mysql>
  17. mysql> SELECT new_abs(0);
  18. +------------+
  19. | new_abs(0) |
  20. +------------+
  21. |          0 |
  22. +------------+
  23. 1 row IN SET (0.00 sec)
  24.  
  25. mysql> SELECT new_abs(1);
  26. +------------+
  27. | new_abs(1) |
  28. +------------+
  29. |          1 |
  30. +------------+
  31. 1 row IN SET (0.00 sec)
  32.  
  33. mysql> SELECT new_abs(-1);
  34. +-------------+
  35. | new_abs(-1) |
  36. +-------------+
  37. |           1 |
  38. +-------------+
  39. 1 row IN SET (0.00 sec)
  40.  
  41. mysql> SELECT new_abs(2147483647);
  42. +---------------------+
  43. | new_abs(2147483647) |
  44. +---------------------+
  45. |          2147483647 |
  46. +---------------------+
  47. 1 row IN SET (0.00 sec)
  48.  
  49. mysql> SELECT new_abs(-2147483647);
  50. +----------------------+
  51. | new_abs(-2147483647) |
  52. +----------------------+
  53. |           2147483647 |
  54. +----------------------+
  55. 1 row IN SET (0.00 sec)
  56.  

SIGN  и аналогични функции има в много езици. Всички други варианти или страдат от проблеми с препълването, или са специфични за дадени компилатор/интерпретатор и архитектура и съответното представяне на отрицателни числа.
Отделно от това, при болшинството решения има неявно деклариране на променлива...
« Последна редакция: Jul 18, 2011, 12:28 от CTEHATA »
Активен

b2l

  • Напреднали
  • *****
  • Публикации: 4786
  • Distribution: MCC Interim
  • Window Manager: - // - // -
  • ...sometimes I feel like screaming... || RTFM!
    • Профил
    • WWW
@CTEHATA поклон, поклон!
Активен

"Човекът е въже, опънато между звяра и свръхчовека, въже над пропаст. Човекът е нещо, което трябва да бъде превъзмогнато." - Фр. Ницше