VC++ is one of the most widely used C++ compilers. The fact that it does not support 80 bit long doubles has apparently convinced a lot of people that such precision is unnecessary - and this wrong idea tends to propagate into other languages, since language designers are rarely familiar with numerical analysis.
The C++ Standard allows this to happen with C++ compilers. It's about time, however, that modern languages have decent, proper support for numerical analysis, and that means requiring the support of the full precision of the underlying hardware. D aims to fully support numerical analysis needs.
If other languages refuse to do so, well, that's another reason why there is a need for D.
80 bit reals are needed for accurate numerical analysis work. They are not needed for games, which SSE2 seems to be targetted at. After all, if a game computes the physics wrong, who cares? It's about time, though, that the needs of numerical analysts are properly supported by a modern language, and D aims to support them fully.
I doubt numerical analysts are using embedded RISC processors. I bet most of them use desktop machines, and those are overwhelmingly x86 based and have hardware 80 bit FPUs on them. And once again, why should analysts who paid for and have high precision hardware on their desk be forced to use floating point dumbed down to the worst FPU on the market? It makes no sense. It's like arguing that a $500 stereo receiver should be deliberately fuzzed so its sound quality isn't better than a transistor radio's.
Your argument also seems to imply that if the input data has error in it, that it doesn't matter if the intermediate calculations introduce more errors in them. Again, this is a serious fundamental mistake in doing numerical analysis. In the example I gave, the engineer made the same argument: the input measurements weren't remotely accurate to 10 digits, they were accurate to 3, so 10 digits for the intermediate results was unnecessary. How wrong that was - his answer was 100% off, all caused by introducing more (roundoff) error with every step of his calculation.
Whenever one uses pointers to members, one always winds up pairing it with a context pointer (this) anyway. Why not eliminate the extra step, and pair it to begin with, and obviate the need for a complex library component? Furthermore, delegates have an untyped context pointer - meaning that the context pointer can be to anything, only the function signature must match. This means that delegates can be nested functions, too. The various C++ attempts to do this require a lot of complexity and overloaded functions.
Pointers to members are useful in their own right.
By themselves, I don't agree. I've almost never seen any use for them in isolation.
A pointer to member plus a pointer to the base object provides all the functionality of function and field delegates.
No, only a subset, as unlike a true delegate, the pointer to member pair is bound to the class type. A delegate is bound to only the function signature.
One of C++'s greatest strengths is that the base language isn't weighted down by functionality that can be implemented in libraries, e.g. text strings. The less redundant pieces that go into a language, the better: libraries are much better for multiple alternatives.
That's an odd thing to say about a language that is, by far, the most complex core language ever devised. BTW, you can devise your own string class in D just as well (or better) than you can in C++. But few find any point in doing so. In any case, C++ does have core text strings and core arrays. They just don't work very well. I also haven't seen any C++ proposals for how to do a string literal in a user defined manner.
Hans Boehm wrote a garbage collector for C/C++ years ago, which happens to be the same one that the Digital Mars implementation of D uses.
While Hans Boehm has written an excellent GC, it has no relationship with D's GC. The complete source to D's GC (which is written 100% in D) comes with D, and you can check it out for yourself.
Contrary to your fact, I used to do numerical analysis work for Boeing when doing actual design work. I wrote my own programs (in FORTRAN) to do it. You're not the first, and I'm sure you won't be the last, to assume I don't know what I'm doing with floating point. You might be interested to know that a number of engineers and mechanics at Boeing tried to prove my results wrong (as well they should, checks and balances are critical to building safe airliners). Either their calculations wound up backing mine, or the test results proved my numbers right.
1. All x86 processors support the 80 bit FPU instructions (either integrated in, or via the add-on x87 FPU). Want me to list them? ST0..7, FADD, FSUB, FCOMPP, etc.
2. With floats, you can invert up to a 7x7 matrix before the results go beserk. With doubles, this goes up to maybe 16. With 80 bit long doubles, 20. There are algorithms to invert larger matrices and account for the roundoff error, but they are complex and unnecessary if you have sufficient precision to begin with. Numerical integration is another problem area that is mitigated by more precision.
3. Isn't x86 mainstream enough for you? It is for me and probably 90% of the computer users out there.
4. Your question shows a serious misunderstanding how floating point roundoff problems affect results. Perhaps I can best illustrate it with a real life example. I was once given the job of double checking another engineer's calculations on a project. The calculation involved about 30 steps, the result of the previous being fed into the next.
My results differed by a factor of 2. That's right, 100% off, not.01% off. So off I went to figure out why. Turns out, the engineer had rounded off each step to two decimal places, before feeding the numbers into the calculator for the next step. After all, as he told me, 2 decimal places was "good engineering precision", and further precision was not necessary. I, on the other hand, used the full 10 digits on my calculator, rounding to 2 decimal places as the very last step. The other engineer simply did not believe that premature rounding was the source of his way-wrong calculations, no matter how I tried to explain it to him. So, I tried a different tact. Working the algrebra of the calculation, I discovered it could be reduced from 30 steps to 3 steps. The 3 steps produced the same answer I had before.
In other works, extra precision is vital, even when more than just "working resolution." Premature roundoff is evil (and if you take a quality physics or engineering course, they'll hammer that into your brain - where I went to college, if you prematurely rounded off anything, you automatically got a big fat 0 for that problem).
Now, to answer your question, as a mechanical engineer designing systems that many lives and millions of dollars were flying on, I didn't give a f*** about portability. I wanted the RIGHT answer. The right answer is much easier to get when there's more precision. I want to use the FULL precision the FPU I'm using can deliver. I'm not using some other crappy FPU, why should my results get compromised by its mere existence? Now, if your customers consider portable wrong answers to be more desirable than correct answers on the machines the customer has, then I sure hope they aren't designing airplane parts.
If there is no ordering relationship between two instances of an object, you can override opEquals and opCmp to throw an exception.
All the data members (including the vptr) to a class are initialized before the constructor is called. The purpose of chaining constructors (a feature proposed for C++) is to be able to avoid code cut & paste between constructors. The class invariant does not need to be satisfied until after the constructor is done, not during its execution.
I have a problem with GC in a systems language... specifically, using GC means that your functions will not necessarily run in bounded time.
malloc (and friends) don't run in bounded time, either.
For a lot of uses, particularly in user space, this is not a problem, but if you were to kick of GC in an interrupt handler or trap handler, or a number of other places, this would make it impossible for you to implement code that was guaranteed to take at most a maximum number of CPU cycles.
You cannot use malloc or new in those circumstances either. The correct way to do it is to preallocate all data needed for the interrupt service routine or real time critical section.
The upshot of this is that so long as it's possible for someone to write a driver that ends up running in your kernel, and which depends on GC functionality to not leak memory, it will be impossible for an OS written in that language to support hard real time.
Hard real time programming uses preallocated or static allocated data, not malloc or new (or GC).
I have to say that GC is marginally useful for systems work only if you can run it on a system that doesn't need GC -- so that you can get a read out of where and how you are leaking memory, fix the problem, and then disable GC before you ship. In other words, it's a great diagnostic, but only if you can run both GC and non-GC at the same time, and only if you explicitly scope your allocations (i.e. act like you are not running in a GC'e lanuage in the first place).
I used to think that, too, until I was forced into working with a GC. I've changed my mind.
In other words, the intent of GC is to make programmers not have to know where their scope boundaries are, and you _must_ know this for systems programming tasks. So it doesn't deliver on its promise in a systems context, though it could be a helpful diagnostic for developers.
All I can suggest is try using a GC for a project. My jawboning won't change your mind, but experience might.
Supplying predefined comparison operators is all very well, but what if a class doesn't support the concept of equality? Alternatively, suppose it supports only equality and not ordering, or vice versa? How do I do that in a natural way, with a single comparison function to define?
There are two comparison operators to overload, opEquals and opCmp. That's how you do it.
The whole concept of relying on scoped variables completely misses a major advantage of RAII, which is that in the common usage, you can't forget anything (a delete, finally or in this case scope) and inadvertently skip the destructor. Requiring some special keywords to get this behaviour is just horrible.
You can declare a class as being a scope class: scope class Foo { }, and it will automatically and always be RAII.
The construction/initialisation semantics just seem a mess. You've either introduced some hideous inefficiency and semantic problems (everything is default-initialised and then reassigned afterwards in the constructor if necessary) or you've introduced a horrible loophole (constructors can start messing around with uninitialised data, for example by calling another member function, before the class invariants are properly set up). The latter is even worse than the analogous loophole in C++.
This is more of a theoretical problem than an actual one. In practice, it just doesn't cause a problem. It also isn't possible to call a member function without the vptr being set up, so the alleged horrible loophole doesn't exist.
In any case, D's claim to this feature is a bit odd, since every x86 C/C++ compiler worth its salt already compiles long double to extended precision.
VC++ doesn't. Java doesn't. C# doesn't. Python doesn't. Ruby doesn't. 80 bit floating point is highly useful, and it's about time it was required for languages on FPUs that support it.
Long double floating point (80bit) This is just desperation. Pretty much no-one uses 80-bit floating point arithmetic IME (and yes, I do work in the field). The portability hazards and lack of true support from almost every mainstream architecture make them almost irrelevant, except perhaps for a few very small niches.
With floating point, more precision is better. It's not just adding a few decimal points; algorithms like numerical integration are easily completely sabotaged by insufficient precision (creeping underflow, gradual accumulation of roundoff errors, etc.) The 80 bit floating point hardware has been on x86 machines for 25 years now. I can't understand how you can characterize that as a lack of true support from almost every mainstream architecture. The x86 pretty much is the mainstream architecture, from the 8087 through the AMD64.
As for portability, if I'm using numerical analysis tools, I freakin' bought hardware that does 80 bits of precision. Why should I want my results dumbed down to the worst FPU built in the last 30 years? Language floating point support should be specified to be the BEST available on the hardware you're running on, not the WORST on someone else's machine.
After all, if you buy a Ferrari you expect to get Ferrari performance, not have it throttled back to that of a 1970's AMC Gremlin.
D OOP features are based on classes, which are reference types. Using value types to implement OOP is fraught with problems, not the least which is the slicing problem. D enables user defined value types with structs. Structs are value types, and do not have OOP features like inheritance and virtual functions.
The trouble with C++ templates is not that they are too powerful, it is that they are not powerful enough for what they are being used for. C++ templates were never designed for metaprogramming. D templates, however, are designed for metaprogramming and it shows. Simple support for tuples, for example, dramatically reduces the implementation size (sometimes by over 90%) of C++ TMP examples.
The debate over whether arrays etc. should be in the core or the library is a long one that has played out in comp.lang.c++.moderated, I refer you to that if you're interested. For this discussion, suffice to say that it is both simplistic and incorrect to say that the existence of core arrays means that such cannot be built with libraries.
Delegates are not just function pointers, they are a pair consisting of a context pointer and a function pointer. The closest thing C++ has to it are member function pointers, which are far more difficult to work with.
D has no VM. This is far more than just whether it is interpreted, jit compiled, or statically compiled. VMs are an abstraction away from the machine, an abstraction that interferes with writing system level code.
(I'm writing this as someone with over ten years of experience in C++, doing everything from protocol stacks to game physics engines to real-time programming. And after ten years, I'm fed up with the mess. This should have been fixed years ago.)
The problem with arrays has been fixed in the D programming language, http://www.digitalmars.com/d/, which is a reengineering of C++.
Re:The D Programming Language
on
Beyond Java
·
· Score: 1
Actaully since you at least have the same name as the creator can you tell me how easy it would be to extend and ebmedd Python with D? Or any other high level languge for that matter?
The D programming language supports the C ABI (Application Binary Interface) which means that any language that supports the C ABI can interact with D.
The D Programming Language
on
Beyond Java
·
· Score: 1
Check out the D programming language, http://www.digitalmars.com/d/, which is a refactoring of C++ to keep the performance benefits but make it much easier to program in.
Since there's GDC, a gnu version of the library, there's no worries about what to do if Digital Mars goes bust. Anyone can continue on with the source, as they can right now.
takes the features of C++, and reengineers them so that one has the power and efficiency of C++, but with fast compilation, true modules, automatic memory management, cleaned up syntax, etc. http://www.digitalmars.com/d/
The "isnot" operator was proposed for the D Programming Language as a replacement for the !== operator already in use in D. The !== operator determines if two references are at the same address or not. Both the "isnot" reference and the !== operator well predate the patent application. isnot proposal earlier D specification
VC++ is one of the most widely used C++ compilers. The fact that it does not support 80 bit long doubles has apparently convinced a lot of people that such precision is unnecessary - and this wrong idea tends to propagate into other languages, since language designers are rarely familiar with numerical analysis.
The C++ Standard allows this to happen with C++ compilers. It's about time, however, that modern languages have decent, proper support for numerical analysis, and that means requiring the support of the full precision of the underlying hardware. D aims to fully support numerical analysis needs.
If other languages refuse to do so, well, that's another reason why there is a need for D.
80 bit reals are needed for accurate numerical analysis work. They are not needed for games, which SSE2 seems to be targetted at. After all, if a game computes the physics wrong, who cares? It's about time, though, that the needs of numerical analysts are properly supported by a modern language, and D aims to support them fully.
I doubt numerical analysts are using embedded RISC processors. I bet most of them use desktop machines, and those are overwhelmingly x86 based and have hardware 80 bit FPUs on them. And once again, why should analysts who paid for and have high precision hardware on their desk be forced to use floating point dumbed down to the worst FPU on the market? It makes no sense. It's like arguing that a $500 stereo receiver should be deliberately fuzzed so its sound quality isn't better than a transistor radio's.
Your argument also seems to imply that if the input data has error in it, that it doesn't matter if the intermediate calculations introduce more errors in them. Again, this is a serious fundamental mistake in doing numerical analysis. In the example I gave, the engineer made the same argument: the input measurements weren't remotely accurate to 10 digits, they were accurate to 3, so 10 digits for the intermediate results was unnecessary. How wrong that was - his answer was 100% off, all caused by introducing more (roundoff) error with every step of his calculation.
Early versions of Win64 did not support the 80 bit x87, but that bug has been fixed.
Whenever one uses pointers to members, one always winds up pairing it with a context pointer (this) anyway. Why not eliminate the extra step, and pair it to begin with, and obviate the need for a complex library component? Furthermore, delegates have an untyped context pointer - meaning that the context pointer can be to anything, only the function signature must match. This means that delegates can be nested functions, too. The various C++ attempts to do this require a lot of complexity and overloaded functions.
Pointers to members are useful in their own right.
By themselves, I don't agree. I've almost never seen any use for them in isolation.
A pointer to member plus a pointer to the base object provides all the functionality of function and field delegates.
No, only a subset, as unlike a true delegate, the pointer to member pair is bound to the class type. A delegate is bound to only the function signature.
One of C++'s greatest strengths is that the base language isn't weighted down by functionality that can be implemented in libraries, e.g. text strings. The less redundant pieces that go into a language, the better: libraries are much better for multiple alternatives.
That's an odd thing to say about a language that is, by far, the most complex core language ever devised. BTW, you can devise your own string class in D just as well (or better) than you can in C++. But few find any point in doing so. In any case, C++ does have core text strings and core arrays. They just don't work very well. I also haven't seen any C++ proposals for how to do a string literal in a user defined manner.
While Hans Boehm has written an excellent GC, it has no relationship with D's GC. The complete source to D's GC (which is written 100% in D) comes with D, and you can check it out for yourself.
1. All x86 processors support the 80 bit FPU instructions (either integrated in, or via the add-on x87 FPU). Want me to list them? ST0..7, FADD, FSUB, FCOMPP, etc.
2. With floats, you can invert up to a 7x7 matrix before the results go beserk. With doubles, this goes up to maybe 16. With 80 bit long doubles, 20. There are algorithms to invert larger matrices and account for the roundoff error, but they are complex and unnecessary if you have sufficient precision to begin with. Numerical integration is another problem area that is mitigated by more precision.
3. Isn't x86 mainstream enough for you? It is for me and probably 90% of the computer users out there.
4. Your question shows a serious misunderstanding how floating point roundoff problems affect results. Perhaps I can best illustrate it with a real life example. I was once given the job of double checking another engineer's calculations on a project. The calculation involved about 30 steps, the result of the previous being fed into the next. My results differed by a factor of 2. That's right, 100% off, not .01% off. So off I went to figure out why. Turns out, the engineer had rounded off each step to two decimal places, before feeding the numbers into the calculator for the next step. After all, as he told me, 2 decimal places was "good engineering precision", and further precision was not necessary. I, on the other hand, used the full 10 digits on my calculator, rounding to 2 decimal places as the very last step. The other engineer simply did not believe that premature rounding was the source of his way-wrong calculations, no matter how I tried to explain it to him. So, I tried a different tact. Working the algrebra of the calculation, I discovered it could be reduced from 30 steps to 3 steps. The 3 steps produced the same answer I had before.
In other works, extra precision is vital, even when more than just "working resolution." Premature roundoff is evil (and if you take a quality physics or engineering course, they'll hammer that into your brain - where I went to college, if you prematurely rounded off anything, you automatically got a big fat 0 for that problem).
Now, to answer your question, as a mechanical engineer designing systems that many lives and millions of dollars were flying on, I didn't give a f*** about portability. I wanted the RIGHT answer. The right answer is much easier to get when there's more precision. I want to use the FULL precision the FPU I'm using can deliver. I'm not using some other crappy FPU, why should my results get compromised by its mere existence? Now, if your customers consider portable wrong answers to be more desirable than correct answers on the machines the customer has, then I sure hope they aren't designing airplane parts.
All the data members (including the vptr) to a class are initialized before the constructor is called. The purpose of chaining constructors (a feature proposed for C++) is to be able to avoid code cut & paste between constructors. The class invariant does not need to be satisfied until after the constructor is done, not during its execution.
malloc (and friends) don't run in bounded time, either.
For a lot of uses, particularly in user space, this is not a problem, but if you were to kick of GC in an interrupt handler or trap handler, or a number of other places, this would make it impossible for you to implement code that was guaranteed to take at most a maximum number of CPU cycles.
You cannot use malloc or new in those circumstances either. The correct way to do it is to preallocate all data needed for the interrupt service routine or real time critical section.
The upshot of this is that so long as it's possible for someone to write a driver that ends up running in your kernel, and which depends on GC functionality to not leak memory, it will be impossible for an OS written in that language to support hard real time.
Hard real time programming uses preallocated or static allocated data, not malloc or new (or GC).
I have to say that GC is marginally useful for systems work only if you can run it on a system that doesn't need GC -- so that you can get a read out of where and how you are leaking memory, fix the problem, and then disable GC before you ship. In other words, it's a great diagnostic, but only if you can run both GC and non-GC at the same time, and only if you explicitly scope your allocations (i.e. act like you are not running in a GC'e lanuage in the first place).
I used to think that, too, until I was forced into working with a GC. I've changed my mind.
In other words, the intent of GC is to make programmers not have to know where their scope boundaries are, and you _must_ know this for systems programming tasks. So it doesn't deliver on its promise in a systems context, though it could be a helpful diagnostic for developers.
All I can suggest is try using a GC for a project. My jawboning won't change your mind, but experience might.
There are two comparison operators to overload, opEquals and opCmp. That's how you do it.
The whole concept of relying on scoped variables completely misses a major advantage of RAII, which is that in the common usage, you can't forget anything (a delete, finally or in this case scope) and inadvertently skip the destructor. Requiring some special keywords to get this behaviour is just horrible.
You can declare a class as being a scope class: scope class Foo { }, and it will automatically and always be RAII.
The construction/initialisation semantics just seem a mess. You've either introduced some hideous inefficiency and semantic problems (everything is default-initialised and then reassigned afterwards in the constructor if necessary) or you've introduced a horrible loophole (constructors can start messing around with uninitialised data, for example by calling another member function, before the class invariants are properly set up). The latter is even worse than the analogous loophole in C++.
This is more of a theoretical problem than an actual one. In practice, it just doesn't cause a problem. It also isn't possible to call a member function without the vptr being set up, so the alleged horrible loophole doesn't exist.
VC++ doesn't. Java doesn't. C# doesn't. Python doesn't. Ruby doesn't. 80 bit floating point is highly useful, and it's about time it was required for languages on FPUs that support it.
Sure, but why not do it right to begin with?
With floating point, more precision is better. It's not just adding a few decimal points; algorithms like numerical integration are easily completely sabotaged by insufficient precision (creeping underflow, gradual accumulation of roundoff errors, etc.) The 80 bit floating point hardware has been on x86 machines for 25 years now. I can't understand how you can characterize that as a lack of true support from almost every mainstream architecture. The x86 pretty much is the mainstream architecture, from the 8087 through the AMD64. As for portability, if I'm using numerical analysis tools, I freakin' bought hardware that does 80 bits of precision. Why should I want my results dumbed down to the worst FPU built in the last 30 years? Language floating point support should be specified to be the BEST available on the hardware you're running on, not the WORST on someone else's machine. After all, if you buy a Ferrari you expect to get Ferrari performance, not have it throttled back to that of a 1970's AMC Gremlin.
D OOP features are based on classes, which are reference types. Using value types to implement OOP is fraught with problems, not the least which is the slicing problem. D enables user defined value types with structs. Structs are value types, and do not have OOP features like inheritance and virtual functions.
The trouble with C++ templates is not that they are too powerful, it is that they are not powerful enough for what they are being used for. C++ templates were never designed for metaprogramming. D templates, however, are designed for metaprogramming and it shows. Simple support for tuples, for example, dramatically reduces the implementation size (sometimes by over 90%) of C++ TMP examples.
The debate over whether arrays etc. should be in the core or the library is a long one that has played out in comp.lang.c++.moderated, I refer you to that if you're interested. For this discussion, suffice to say that it is both simplistic and incorrect to say that the existence of core arrays means that such cannot be built with libraries.
Delegates are not just function pointers, they are a pair consisting of a context pointer and a function pointer. The closest thing C++ has to it are member function pointers, which are far more difficult to work with.
D has no VM. This is far more than just whether it is interpreted, jit compiled, or statically compiled. VMs are an abstraction away from the machine, an abstraction that interferes with writing system level code.
The problem with arrays has been fixed in the D programming language, http://www.digitalmars.com/d/, which is a reengineering of C++.
in Digital Mars C++. See contracts.
If you want contract programming, but prefer C++ style syntax to Eiffel style, try the D programming language.
http://www.digitalmars.com/d/
The D programming language supports the C ABI (Application Binary Interface) which means that any language that supports the C ABI can interact with D.
Check out the D programming language, http://www.digitalmars.com/d/, which is a refactoring of C++ to keep the performance benefits but make it much easier to program in.
I got tired of complaining about C++, and decided to do something about it. The result is the D programming language.
Since there's GDC, a gnu version of the library, there's no worries about what to do if Digital Mars goes bust. Anyone can continue on with the source, as they can right now.
Actually, it is free. GDC is the gnu version of the compiler, completely GPL.
D is a refactoring of C++ to make it easier, more powerful, and more robust to use. -Walter
takes the features of C++, and reengineers them so that one has the power and efficiency of C++, but with fast compilation, true modules, automatic memory management, cleaned up syntax, etc. http://www.digitalmars.com/d/
. php?test=all&lang=all&sort=fullcpu
For performance of D, see http://shootout.alioth.debian.org/great/benchmark
The "isnot" operator was proposed for the D Programming Language as a replacement for the !== operator already in use in D. The !== operator determines if two references are at the same address or not. Both the "isnot" reference and the !== operator well predate the patent application.
isnot proposal
earlier D specification
and it's called the D Programming Language. It has Design by Contract and unit testing built in to a C++ like language.