News for hackers. Stuff that matters.
Re:On the evolution of programming style: How far C++ has mo... by alextgordon on Monday March 26, 2012 @11:43AM (#3757175)
Let me give one other way of doing it: "C++ without classes"

    std::pair<double, double> findRoot(double a, double b, double c) {
        if (a == 0.0)
            throw std::invalid_argument("a has value 0.0!");
    
        double root1 = (-b + sqrt(b*b - 4*a*c)) / (2*a);
        double root2 = (-b - sqrt(b*b - 4*a*c)) / (2*a);
    
        return std::pair<double, double>(root1, root2);
    }
It seems too many C++ programmers see a problem and think "Hey, let's use classes!".
Re:On the evolution of programming style: How far C++ has mo... by dalke on Monday March 26, 2012 @01:51PM (#3757805)
The linked page say "The typical C solution is to create a structure to hold the quadratic equation parameters." That seems like sticking 'C with classes' into something which doesn't need classes in the first place. Why wouldn't the standard solution for C use pointers to pass the return parameters? Something like:

    #include <math.h>
    #include <errno.h>
    int findRoot(double a, double b, double c, double *root1, double *root2) {
        double discriminant, q;
        if (a == 0.0) {
            return EDOM;
        }
        discriminant = b*b-4*a*c;
        if (discriminant < 0) {
            return EDOM;
        }
        q = -(b+copysign(sqrt(discriminant), b)) / 2;
        *root1 = q/a;
        *root2 = c/q;
        return 0;
    }
This style is quite in line with a number of math packages I've used in C. Since the author used a numerically unstable solution, I suspect there's a lack of domain inexperience in what library users expect from a math package. Eg., why would I go through the overhead of copying my input parameters to a temporary struct when I could just pass them directly to the function?
Re:On the evolution of programming style: How far C++ has mo... by ansgri on Monday March 26, 2012 @03:16PM (#3758208)
Make it a template (parameterized with coefficient and solution type) and you'll get very generic and efficient solution across all mentioned 'dialects' of c(++). It has the added bonus that this routine can be converted to vector form almost without breaking the interface: just make a, b, c pointers and specify length. And you can specialize templates when you'll want to rewrite float32_t-version in assembly.

Numeric code is really inappropriate for demonstration of new C++ features, as good (fast!) numeric code is most often C with templates.

Re:On the evolution of programming style: How far C++ has mo... by ajax77 on Monday March 26, 2012 @04:29PM (#3758493)
A slight variation with neither error codes nor exceptions.

  template< typename T >
  auto findRoots(T a, T b, T c) -> pair<maybe<complex<T>>, maybe<complex<T>>> {
    typedef complex<T>       C;
    typedef maybe<C>         Root;
    typedef pair<Root, Root> Roots;

    if (a == 0) {
      if (b == 0) 
        return Roots(nothing,      nothing);
      else
        return Roots(Root(-c / b), nothing);
    } else {
      auto d     = C(b*b - a*c*4);
      auto two_a = a*2;
      return Roots(Root((-b + sqrt(d)) / two_a), 
                   Root((-b - sqrt(d)) / two_a));
    }
  }
  
maybe is simply a templatized std::pair wrapper that lets you check if the returned value is valid (either via maybe.valid() or if(maybe)). All imaginary results are handled automatically via std::complex. nothing decomposes into an invalid maybe value of the appropriate type.
Re:On the evolution of programming style: How far C++ has mo... by dalke on Tuesday March 27, 2012 @11:05AM (#3761849)
The minor nit first: the original specification did not support complex numbers, so adding them here is even less relevant than my return codes.

More importantly, your implementation is numerically less correct than my version, which itself is less correct than it should be.

Consider when b is near sqrt(d). In that case, b-sqrt(d) can lose precision. That's why I used the copysign function, so that I'm always adding two numbers of equal sign and size.

Mine is incorrect if b2 is near the size of 4ac since there too b2-4ac loses precision. Ideally this intermediate result should be done in quad precision if the input is in double.

Question: How does C++ handle copysign() (vs. copysignf() for floats), and support templates which want to use an higher precision intermediate value?

Re:On the evolution of programming style: How far C++ has mo... by ajax77 on Tuesday March 27, 2012 @02:06PM (#3762831)
My reply wasn't meant as a numerically superior solution, simply as a fairly simple alternative without error codes...

As for "original specification", I'm not sure why you even bring that up. complex is certainly in the C++11 specification, against which my snippet is compliant.

Re:On the evolution of programming style: How far C++ has mo... by dalke on Tuesday March 27, 2012 @04:01PM (#3763339)
Oh, I see I didn't explain well enough. I'm curious about how one would write the "numerically superior solution." That is, does C++11 have a templated copysign function which takes different numeric types (at least float, double, and quad)? If not, then there's some unneeded type promotion (or downcast) going on.

And if the template uses a float, how do I get the type with double precision to use as my intermediate? (And the same where the template uses a double and I want the intermediate to use a quad.)

There must surely be a way to handle these, but I haven't done C++ programming for over a decade and I don't know the modern way of doing things.

As regards "original specification" - I mean the original article from feabhas.com, which has since disappeared. As I recall, it only supported real roots, and not imaginary ones.

BTW, _Complex is also in C99.

Re:On the evolution of programming style: How far C++ has mo... by matthavener on Monday March 26, 2012 @11:54AM (#3757233)
Reminds me of one of my favorite all time John Carmack tweets:

"Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function."

https://twitter.com/#!/ID_AA_Carmack/status/5351230045120102...

Re:On the evolution of programming style: How far C++ has mo... by marshray on Monday March 26, 2012 @02:21PM (#3757944)
Exactly. The function is perhaps the most powerful and useful abstraction in all of computer science.

I always found it absurd that some languages force you to write "Math.cos(...)".

Re:On the evolution of programming style: How far C++ has mo... by slowpoke on Monday March 26, 2012 @03:13PM (#3758190)
>I always found it absurd that some languages force you to write "Math.cos(...)".

I disagree. Namespaces are a good thing. The "Foo dot bar" syntax is just commonly associated with class member access. I think it's a lot better to have to explicitly refer to where a function comes from (as long as it doesn't devolve into retarded Java-style verbosity, ie "Foo.bar.baz.spam.eggs..."). Though it's nice to have a way to omit the namespace, ie C++'s "using namespace Foo" or Python's "from Foo import bar". I just think that the sane default is to use explicit namespaces.

Re:On the evolution of programming style: How far C++ has mo... by marshray on Monday March 26, 2012 @05:22PM (#3758708)
I don't think everyone should always use explicit namespaces everywhere.

But in this case "Math" isn't even a namespace! It's a class of which cos() is a static member. http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html...

Re:On the evolution of programming style: How far C++ has mo... by sukuriant on Monday March 26, 2012 @07:14PM (#3759094)
In a recent article on Hacker News regarding the evils of classes, one of the valid uses of classes were for makeshift namespaces. I would have to agree with this. The model of Java requires all functions to be inside classes. If you have a class full of statics, it functionally becomes a unique namespace. It's semantics, but at that point, they're interchangeable.
Re:On the evolution of programming style: How far C++ has mo... by qu4z-2 on Monday March 26, 2012 @11:57PM (#3759947)
Except that you can't import a class using "using" or "import", so you have to write Math.cos() everywhere.

EDIT: I see below that Java has added "import static", but at that point you're in the situation where you have two things that are almost the same but not quite, which is not a particularly clean design in my opinion.

Re:On the evolution of programming style: How far C++ has mo... by SeanLuke on Monday March 26, 2012 @07:58PM (#3759239)
You are conflating namespaces (a general and abstract concept) with Java packages. While it's true that static methods can have private static (essentially global) data, in in the Java standard libraries static methods largely exist as a mechanism to keep function names from polluting a common global namespace.

And Math is a particularly special case of this. Ponder this: have you ever had to say

    import java.lang.Math

?
Re:On the evolution of programming style: How far C++ has mo... by pauldino on Monday March 26, 2012 @06:40PM (#3758994)
Thankfully Java 5 added 'import static' which lets you pretend classes are namespaces.

http://docs.oracle.com/javase/1.5.0/docs/guide/language/stat...

Re:On the evolution of programming style: How far C++ has mo... by marshray on Monday March 26, 2012 @06:45PM (#3759004)
Thanks for that info. Anyone know if C# has something similar?

From the linked docs: So when should you use static import? Very sparingly!

To me, it sort of confirms the point that these inelegant features are necessary because the language doesn't support fundamental primitives like plain functions and named constants without a superfluous object definition.

Re:On the evolution of programming style: How far C++ has mo... by pauldino on Tuesday March 27, 2012 @12:44AM (#3760054)
The CLI itself actually supports unadorned functions, but C# doesn't provide a way of writing them, or using directly them without resorting to reflection. I'm pretty sure C++/CLI lets you use and write standalone functions in managed code, and come to think of it I imagine F# would too. Also it's not an exact equivalent, but C# extension methods can often be used instead of static methods.
Re:On the evolution of programming style: How far C++ has mo... by atesti on Wednesday March 28, 2012 @06:14PM (#3768643)
You can mark a class as static, then it will only allow static functions or members:

static class Math { static int Pow(int a,int b) {...} }

Re:On the evolution of programming style: How far C++ has mo... by alexk7 on Monday March 26, 2012 @01:46PM (#3757777)
This is even more C++11-ish: (Not tested, my compiler at work is not C++11 compliant.)

    constexpr tuple<double, double> Roots(double a, double b, double c)
    {
        return (a == 0.0) ? throw invalid_argument("a has value 0.0!") :
            tuple<double, double>{
                (-b + (sqrt((b * b) - 4 * a * c))) / 2*a,
                (-b - (sqrt((b * b) - 4 * a * c))) / 2*a
            };
    }
Re:On the evolution of programming style: How far C++ has mo... by jsnell on Monday March 26, 2012 @02:51PM (#3758090)
That's just horrible. The readability of the function has been totally destroyed by conditional operator. What's the imaginary C++11-ish benefit?
Re:On the evolution of programming style: How far C++ has mo... by alexk7 on Monday March 26, 2012 @04:36PM (#3758518)
With constexpr, if the parameters are constants, the whole computation can happen at compile-time. The line is somewhat harder to read for people not used to functional programming, but unfortunately, constexpr functions are required to have a single expression.
Re:On the evolution of programming style: How far C++ has mo... by hythloday on Monday March 26, 2012 @03:41PM (#3758304)
Is

  return /* ... */ throw invalid_argument(""); 
really valid C++11?
Re:On the evolution of programming style: How far C++ has mo... by alexk7 on Monday March 26, 2012 @04:38PM (#3758530)
"The rules for conditional operator are quite sophisticated and one of them says that if one of the sub-expressions is a throw expression, the type of the entire conditional operator is that of the other sub-expression." [1]

[1] http://akrzemi1.wordpress.com/2011/05/06/compile-time-comput...

Re:On the evolution of programming style: How far C++ has mo... by hythloday on Monday March 26, 2012 @07:59PM (#3759245)
Thanks for both your and pdw's explanations...the lovely thing about C++ is that there's always something new to learn. ;)
Re:On the evolution of programming style: How far C++ has mo... by pdw on Monday March 26, 2012 @05:11PM (#3758666)
It's valid in all versions of C++, I believe. It's usually motivated with a chained ternary operator:

  return a == ALPHA ? x :
         a == BETA ? y :
         a == GAMMA ? z :
         throw ...;
Although that's not a style I'd recommend.
Re:On the evolution of programming style: How far C++ has mo... by colomon on Monday March 26, 2012 @12:04PM (#3757278)
Ha! I was going to post a Perl 6 version of this to show how much shorter it is when you don't go through all the rigamarole, but of course you remind us that it's that short in C++ if you do it in a sensible manner. Bravo.
Re:On the evolution of programming style: How far C++ has mo... by YogSothoth on Monday March 26, 2012 @01:43PM (#3757762)
I'm with you alex, I tend to only use classes when I've got to maintain state, I just use a function otherwise. Here's how I'd likely code this ...

  #include <iostream>
  #include <string>
  #include <cmath>
  
  using std::string;
  using std::cout;
  using std::endl;
  
  static bool findRoots(
     double a,
     double b,
     double c,
     double &rootOne,
     double &rootTwo,
     string &errMsg
  )
  {
     if(a == 0.0)
     {
        errMsg = "Parameter 'a' cannot be zero";
        return(false);
     }
  
     double discriminant = b * b - 4 * a * c;
  
     if(discriminant < 0.0)
     {
        errMsg = "Discriminant cannot be less than zero";
        return(false);
     }
  
     double dSqrt = sqrt(discriminant);
     double twoA  = 2 * a;
  
     rootOne = (-b + dSqrt) / twoA;
     rootTwo = (-b - dSqrt) / twoA;
  
     return(true);
  }
  
  int main()
  {
     double a[] = { 0.0, 2.0, 2.0 };
     double b[] = { 1.0, 3.0, 1.0 };
     double c[] = { 2.0, 2.0, 0.0 };
  
     double rootOne;
     double rootTwo;
     string errMsg;
  
     for(int n = sizeof(a) / sizeof(a[0]), i = 0; i < n; i ++)
     {
        cout << "a: " << a[i] << ", b: " << b[i] << ", c: " << c[i] << endl;
  
        if(findRoots(a[i], b[i], c[i], rootOne, rootTwo, errMsg))
        {
           cout << rootOne << " " << rootTwo << endl;
        }
        else
        {
           cout << errMsg << endl;
        }
  
        cout << endl;
     }
  
     return(0);
  }
Re:On the evolution of programming style: How far C++ has mo... by VMG on Tuesday March 27, 2012 @08:04AM (#3761038)
thank you for your answer, but next time better use something like http://gist.github.com
Re:On the evolution of programming style: How far C++ has mo... by dxbydt on Monday March 26, 2012 @12:52PM (#3757526)
if you are comfortable with returning tuples, perhaps the inputs should be a tuple as well...

     def findRoot(p:(Double,Double,Double)) = {
       assert(p._1 != 0)
       val n = sqrt(p._2*p._2-4*p._1*p._3)
       val d = 2*p._1
       ( (-p._2+n)/d, (-p._2-n)/d )
     }
      ----
      scala> findRoot((6,-6,-36))
      res0: (Double, Double) = (3.0,-2.0)
Re:On the evolution of programming style: How far C++ has mo... by ajross on Monday March 26, 2012 @01:48PM (#3757789)
Not sure how we got to Scala, but in (non-varargs) C linkage the whole metaphor of "funtion arguments" is inherently a "tuple" to begin with. They only exist in tandem with each other, never in isolation. There's no real value I can see to trying to double-abstract it.
Re:On the evolution of programming style: How far C++ has mo... by nooop on Monday March 26, 2012 @02:05PM (#3757862)
> if you are comfortable with returning tuples, perhaps the inputs should be a tuple as well...

why should they?

Re:On the evolution of programming style: How far C++ has mo... by cbsmith on Monday March 26, 2012 @05:23PM (#3758710)
Honestly, given the problem context, a function is obviously the way to go. There isn't enough other context to give you a guideline for having an object model.

In a larger context, you might want to actually have an object/struct that represents a quadratic equation. The weird thing though was having an object for a quadratic equation and not having another object for its root (or more likely, either have a binomial class or just one generic polynomial class). I'm trying to think of a context where that makes any sense at all... and not finding it.

Re:On the evolution of programming style: How far C++ has mo... by naner on Monday March 26, 2012 @04:22PM (#3758464)
It seems too many C++ programmers see a problem and think "Hey, let's use classes!".

That's usually a result of people coming from Java doing C++ work.

Re:On the evolution of programming style: How far C++ has mo... by eps on Monday March 26, 2012 @06:34PM (#3758968)
That's usually a result of people erroneously thinking of themselves as C++ experts.
Re:On the evolution of programming style: How far C++ has mo... by alexchamberlain on Monday March 26, 2012 @12:23PM (#3757370)
What's std::pair if it isn't a class?
Re:On the evolution of programming style: How far C++ has mo... by ajross on Monday March 26, 2012 @12:43PM (#3757470)
Surely alextgordon's point was that defining a solution in terms of a class where a function was more appropriate was bad, not that use of existing abstract types is to be avoided.
Re:On the evolution of programming style: How far C++ has mo... by alextgordon on Monday March 26, 2012 @12:33PM (#3757421)
There's nothing inherently wrong with classes. I use classes occasionally. But if you don't need to use one for everything. C++ isn't Java.
Re:On the evolution of programming style: How far C++ has mo... by YogSothoth on Monday March 26, 2012 @01:23PM (#3757680)
Re:On the evolution of programming style: How far C++ has mo... by alexchamberlain on Monday March 26, 2012 @03:07PM (#3758165)
I was waiting for that! :-P