rational numbers

216 views
Skip to first unread message

alessandro volturno

unread,
Sep 16, 2021, 9:00:13 AMSep 16
to
Hello group,

I am writing some C++ code to implement a class that handles fractional
numbers.
C++11 has the Ratio facility, but it is not possible to instantiate
Ratio objects or pass them to functions (at least I was not able to do
that), so just for fun I started a simple rational class, adding the
following:

private members *num* and *den*

constructor;
destructor; // defaulted
copy assignment operator // defaulted
copy constructor // defaulted
move assignment operator // defaulted
move constructor // defaulted

operator+ ()
operator- ()
operator* ()
operator/ ()
operator== ()
operator!= ()

friend operator<= ()
friend operator< ()
friend operator> ()
friend operator>= ()

friend operator<< ()
friend operator>> ()

printDescription() // prints a description of the number and its

// numerical value

reduce() // simplifies a fraction using a static GCD function

reciprocal() // inverts a rational number


Maybe my question is stupid, but
why not to introduce a way to handle rational numbers inside the C++
standard or better make it a built in type?

Paavo Helde

unread,
Sep 16, 2021, 9:40:26 AMSep 16
to
16.09.2021 15:59 alessandro volturno kirjutas:
>
> Maybe my question is stupid, but
> why not to introduce a way to handle rational numbers inside the C++
> standard or better make it a built in type?

What are the use cases for rationals? If something is not in the
standard, then often there are different use cases which cannot be
easily covered by a common "standard" implementation.

For rationals it feels like a fast implementation would have a pretty
limited numeric range, and unlimited range would require arbitrary
precision integers and would probably be many times slower even in case
of small numbers.

Nevertheless, there is at least one proposal to include rationals in the
standard, see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3363.html

Meanwhile one can use the Boost Rational library.

alessandro volturno

unread,
Sep 16, 2021, 12:25:45 PMSep 16
to
Il 16/09/2021 15:40, Paavo Helde ha scritto:
> 16.09.2021 15:59 alessandro volturno kirjutas:
>>
>> Maybe my question is stupid, but
>> why not to introduce a way to handle rational numbers inside the C++
>> standard or better make it a built in type?

I speak as a non-competent, hobbyist programmer and computer-user.

> What are the use cases for rationals? If something is not in the
> standard, then often there are different use cases which cannot be
> easily covered by a common "standard" implementation.

Computers were born to manipulate numbers and the languages for doing
that, at that time, were mainly two: FORTRAN, and LISP. I have noticed
that C++ (that I presume it could be thought of as a descendant of
FORTRAN, is shifting towards Functional programming language facilities,
like that offered by the LISP family. That's good, but numerical
programming is still important nowadays.

> For rationals it feels like a fast implementation would have a pretty
> limited numeric range, and unlimited range would require arbitrary
> precision integers and would probably be many times slower even in case
> of small numbers.

Mine uses long long int, that is the maximum offered by the language,
but calculation speed is constantly increasing. I don't see that a as a
limiting factor.

True that C++ applications can be built for every available hardware
configuration (and so they can be run on older or slower cpus), but
rational are an important class of numbers and its family is wider than
integers'

> Nevertheless, there is at least one proposal to include rationals in the
> standard, see
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3363.html

The proposal you presented is quite dated, so I presume many talkings
have been made about it. I don't know the reason why, but it evidently
came out not to add this feature to the language.

Many computer languages are evolving and new ones are born every one or
two years. In my opinion, to keep a language competitive with respect to
the others around, that language should also permit basic number
manipulation as easier as possible.

I repeat myself, I'm a hobbyist, spare-time programmer, but as complex
numbers have been added to C++, so there should be some mechanics to
handle rationals.

> Meanwhile one can use the Boost Rational library.

I've heard about the Boost library but I've never made an attempt to
study it nor to use it.

My projects are small ones and I write them by scratch just for the sake
of knowing how I progress in this discipline (computer programming).

Thank you for your kind reply,

alessandro

Scott Lurndal

unread,
Sep 16, 2021, 1:05:20 PMSep 16
to
alessandro volturno <alessandr...@libero.it> writes:
>Il 16/09/2021 15:40, Paavo Helde ha scritto:
>> 16.09.2021 15:59 alessandro volturno kirjutas:
>>>
>>> Maybe my question is stupid, but
>>> why not to introduce a way to handle rational numbers inside the C++
>>> standard or better make it a built in type?
>
>I speak as a non-competent, hobbyist programmer and computer-user.
>
>> What are the use cases for rationals? If something is not in the
>> standard, then often there are different use cases which cannot be
>> easily covered by a common "standard" implementation.
>
>Computers were born to manipulate numbers and the languages for doing
>that, at that time, were mainly two: FORTRAN, and LISP. I have noticed
>that C++ (that I presume it could be thought of as a descendant of
>FORTRAN, is shifting towards Functional programming language facilities,
>like that offered by the LISP family. That's good, but numerical
>programming is still important nowadays.

I suspect that the vast majority of people use existing
software packages like Matlab or R (for statistics) than write C++ code
for numerical software nowadays. It's really hard to get it
right when using binary floating point, hence standard numerical
libraries (LINPACK, et alia).

https://en.wikipedia.org/wiki/List_of_numerical_libraries

Manfred

unread,
Sep 16, 2021, 1:58:28 PMSep 16
to
It is true that numerical analysis is easier in Matlab than C++, but I
think the main point is that, if you know what you are doing, standard
floating point and integer types can cover the a lot of application use
even when writing C++ (as well as C) programs.

The main advantage of rational arithmetic is that it allows an exact
representation of a significant class of numbers, without rounding errors.
But in many practical applications (engineering, automation, ...) 16
significant digits (as offered by the 'double' type) yield rounding
errors that are small enough.
It is true, though, that specific knowledge is required - naive code can
easily yield garbage.

The limitation that I see with digital rationals, without having read
the linked proposal, is that they would still represent a limited subset
of the rational numbers, which is a limitation for theoretical analysis
- in addition, output results can be exact only if inputs are exact
rationals numbers, which rules out all physical quantities, a limitation
of the advantage over floating point math.

In short, it is understandable that there is not enough motivation to
include this as a native type in the language, compared to what is
already available through libraries.

David Brown

unread,
Sep 16, 2021, 2:42:13 PMSep 16
to
On 16/09/2021 18:24, alessandro volturno wrote:
> Il 16/09/2021 15:40, Paavo Helde ha scritto:
>
>> For rationals it feels like a fast implementation would have a pretty
>> limited numeric range, and unlimited range would require arbitrary
>> precision integers and would probably be many times slower even in
>> case of small numbers.
>
> Mine uses long long int, that is the maximum offered by the language,
> but calculation speed is constantly increasing. I don't see that a as a
> limiting factor.
>
> True that C++ applications can be built for every available hardware
> configuration (and so they can be run on older or slower cpus), but
> rational are an important class of numbers and its family is wider than
> integers'

There are two key difficulties about constructive use of rationals. One
is that you have to handle reduction by greatest common denominator -
that is not a simple operation, but is time-consuming and has
unpredictable timing. The other is that the sizes of the denominator
and numerator very quickly get very big for all but the simplest of
calculations - you don't have to do a lot with them before the
arithmetic becomes impossible within the fixed size framework. (This is
in comparison to complex numbers, which are easy.)

Basically, I don't think there is that much need of rationals of fixed
size - you can usually make do with floating point or integers, or you
need arbitrary precision. Rationals are very important in mathematics,
much less so in practical programming.

However, as a hobby task, a good set of rational number classes would be
great fun to work on. You can start with a basic implementation, then
work on a templated version handling different sizes, learn about
concepts with a practical use-case, figure how to make everything
constexpr, get neat ways to make bigger fixed-size rational types,
explore ways to handle overflows. The scope for enjoyment and learning
is huge here.

Bart

unread,
Sep 16, 2021, 4:12:15 PMSep 16
to
On 16/09/2021 19:41, David Brown wrote:
> On 16/09/2021 18:24, alessandro volturno wrote:
>> Il 16/09/2021 15:40, Paavo Helde ha scritto:
>>
>>> For rationals it feels like a fast implementation would have a pretty
>>> limited numeric range, and unlimited range would require arbitrary
>>> precision integers and would probably be many times slower even in
>>> case of small numbers.
>>
>> Mine uses long long int, that is the maximum offered by the language,
>> but calculation speed is constantly increasing. I don't see that a as a
>> limiting factor.
>>
>> True that C++ applications can be built for every available hardware
>> configuration (and so they can be run on older or slower cpus), but
>> rational are an important class of numbers and its family is wider than
>> integers'
>
> There are two key difficulties about constructive use of rationals. One
> is that you have to handle reduction by greatest common denominator -
> that is not a simple operation, but is time-consuming and has
> unpredictable timing. The other is that the sizes of the denominator
> and numerator very quickly get very big for all but the simplest of
> calculations - you don't have to do a lot with them before the
> arithmetic becomes impossible within the fixed size framework. (This is
> in comparison to complex numbers, which are easy.)

You get a similar problem with arbitrary precision floats. (At least, in
my library. I don't know how others deal with it; I use upper limits on
precision).

Multiply two numbers with M and N digits of precision respectively, and
the result will have M*N digits of precision, without the magnitude
necessarily getting bigger.

It's worse with divide: just do 1.0/3.0, and it will be calculating
0.3333.... forever (the theoretical limit in mine is 36 billion digits,
but it will be aborted, long before).

At least with rationals, this can be deferred, or cancelled out if the
next thing is to multiply by 3 again.


> Basically, I don't think there is that much need of rationals of fixed
> size - you can usually make do with floating point or integers, or you
> need arbitrary precision. Rationals are very important in mathematics,
> much less so in practical programming.

> However, as a hobby task, a good set of rational number classes would be
> great fun to work on. You can start with a basic implementation, then
> work on a templated version handling different sizes, learn about
> concepts with a practical use-case, figure how to make everything
> constexpr, get neat ways to make bigger fixed-size rational types,
> explore ways to handle overflows. The scope for enjoyment and learning
> is huge here.

I've often reserved a rational type in the languages I create, but have
never got round to implementing them. They will usually need a
big-integer type for a start.

But as you say they are not that practical. How would they be displayed
for example; as proper and improper fractions? I get enough grief when
my Casio gets stuck in fraction mode and I can't remember how to fix it.


Paavo Helde

unread,
Sep 16, 2021, 4:13:15 PMSep 16
to
16.09.2021 19:24 alessandro volturno kirjutas:
> Il 16/09/2021 15:40, Paavo Helde ha scritto:
>> 16.09.2021 15:59 alessandro volturno kirjutas:
>>>
>>> Maybe my question is stupid, but
>>> why not to introduce a way to handle rational numbers inside the C++
>>> standard or better make it a built in type?
>
> I speak as a non-competent, hobbyist programmer and computer-user.
>
>> What are the use cases for rationals? If something is not in the
>> standard, then often there are different use cases which cannot be
>> easily covered by a common "standard" implementation.
>
> Computers were born to manipulate numbers and the languages for doing
> that, at that time, were mainly two: FORTRAN, and LISP. I have noticed
> that C++ (that I presume it could be thought of as a descendant of
> FORTRAN, is shifting towards Functional programming language facilities,
> like that offered by the LISP family. That's good, but numerical
> programming is still important nowadays.

Of course numerical programming is important. It's done mostly in
floating-point (64-bit and 32-bit; in recent years also 16-bit on
GPU-s). In some cases it can be done in integers, for more speed, but
it's not so simple and the speedups are not very large any more nowadays.

I notice that you still haven't presented any use case for rationals. I
have to admit I also wrote a C++ class for rational numbers ca 20 years
ago, but so far I have not found any usage for it in my work (which also
involves a lot of heavy numeric computations).

>
>> For rationals it feels like a fast implementation would have a pretty
>> limited numeric range, and unlimited range would require arbitrary
>> precision integers and would probably be many times slower even in
>> case of small numbers.
>
> Mine uses long long int, that is the maximum offered by the language,
> but calculation speed is constantly increasing. I don't see that a as a
> limiting factor.

Long long int probably means 64 bits, which is not so much. The smallest
positive rational would be 1/2^64, i.e. ca 10^-61. Meanwhile, the
smallest positive double is ca 10^-308, with the same number of bits.
Ditto for the largest values. IOW, floating-point can approximate values
with much more precision, and there is much less danger of overflows
during computations.

The only advantage what rationals offer above floating-point is that the
calculation results are always exact. However, in numeric programming
the input data often comes from a physical measurement, meaning that it
already contains measurement inaccuracies. Thus the end result will not
be absolutely accurate anyway, so there is no need to use absolutely
precise computations.

Sure, with floating-point algorithms one must take care to not lose too
much precision in intermediate results. However, I suspect that with
rational number algorithms even more care is needed, to avoid numeric
overflows in intermediate results.

[...]
> Thank you for your kind reply,

Thanks!


Ben Bacarisse

unread,
Sep 16, 2021, 4:26:36 PMSep 16
to
Bart <b...@freeuk.com> writes:

> You get a similar problem with arbitrary precision floats. (At least,
> in my library. I don't know how others deal with it; I use upper
> limits on precision).
>
> Multiply two numbers with M and N digits of precision respectively,
> and the result will have M*N digits of precision, without the
> magnitude necessarily getting bigger.

That sounds odd. If it's binary FP, multiplying 2 by 2 need not result
in a result with more digits than the already exact operands. Do you
always widen the mantissa in a multiplication?

--
Ben.

Bart

unread,
Sep 16, 2021, 4:55:10 PMSep 16
to
(My library is decimal, but there will be the same issues with binary.)

I think I meant M+N, not M*N! And that's approximate. Squaring a single
digit 1-9 will result in 1-81, which is 1 or 2 digits. (M*N applies to
exponentiation.)

In any case, it will increase the number of digits when chaining a lot
of multiplications, when you have no upper limit on precision.

There is some confusion since digits or bits don't usually exist by
themselves, but grouped into words or 'limbs'. Squaring 31 bits of value
contained within a 64-bit type yields something that still fits into 64
bits, but may need 61/62 of those bits to represent.

But this is about arbitrary precision with multiple words to store results.

Alf P. Steinbach

unread,
Sep 16, 2021, 5:08:18 PMSep 16
to
I think Ben meant to write M + N, not M*N. :-o

Consider the product of A = a*r^n and B = b*r^m, where a and b are
integers and r is the numeral system radix.

You get a result C = A*B = a*b*r^(n+m), where the number of digits of C
is roughly (the number of digits of a) + (the number of digits of b).


- Alf

Alf P. Steinbach

unread,
Sep 16, 2021, 5:27:51 PMSep 16
to
I evidently meant to write "Bart", not "Ben".

Oh well.


- Alf


Juha Nieminen

unread,
Sep 17, 2021, 1:37:14 AMSep 17
to
alessandro volturno <alessandr...@libero.it> wrote:
> printDescription() // prints a description of the number and its
> // numerical value

This is not really something that belongs to a class that behaves like an
arithmetic numerical value.

At most what you could have is a separate

std::ostream& operator<<(std::ostream&, YourRationalClass);

function for outputting the value to a std::ostream.

Christian Gollwitzer

unread,
Sep 17, 2021, 3:21:22 AMSep 17
to

Am 16.09.21 um 19:05 schrieb Scott Lurndal:
> I suspect that the vast majority of people use existing
> software packages like Matlab or R (for statistics) than write C++ code
> for numerical software nowadays. It's really hard to get it
> right when using binary floating point, hence standard numerical
> libraries (LINPACK, et alia).

I am a scientist, and Matlab and R are popular, but slowly they give way
to Python. With the "numpy" package wrapping LAPACK and "scipy", which
is based on it, Python code almost looks like Matlab, but it's
completely open source. To my understanding, that and the machine
learning libs are the reasons why Python gained so much in the last
years. The language on its own is not that much better than other modern
scripting languages, it's the libraries with a large user base.

Christian

alessandro volturno

unread,
Sep 17, 2021, 3:52:05 AMSep 17
to
Il 16/09/2021 22:12, Paavo Helde ha scritto:
> 16.09.2021 19:24 alessandro volturno kirjutas:
>> Il 16/09/2021 15:40, Paavo Helde ha scritto:

>>> What are the use cases for rationals? If something is not in the
>>> standard, then often there are different use cases which cannot be
>>> easily covered by a common "standard" implementation.

> I notice that you still haven't presented any use case for rationals. I
> have to admit I also wrote a C++ class for rational numbers ca 20 years
> ago, but so far I have not found any usage for it in my work (which also
> involves a lot of heavy numeric computations).

I am not a mathematician nor a physicist but probably solving systems of
linear equations with Gauss or Gauss-Jordan methods can be an
application of rational numbers.

Many years ago trying to use the Common lisp programming language, I was
impressed by its natural handling of rational numbers and by its use of
integers or floating point numbers of arbitrary precision. Common Lisp
is an ANSI standard dated 1990 but it inherits properties and behaviours
from ancient LISP dialects. So it seemed to me a bit strange that a
programming language as widespread as C++ doesn't offer that same
facilities.

> The only advantage what rationals offer above floating-point is that the
> calculation results are always exact. However, in numeric programming
> the input data often comes from a physical measurement, meaning that it
> already contains measurement inaccuracies. Thus the end result will not
> be absolutely accurate anyway, so there is no need to use absolutely
> precise computations.

there are not only physical measures, you could develop examples with
integer numbers just for didactic goals. C++ must be learned like any
other discipline. And having wide numerical facilities can be very
useful permitting to develop new strategies by tackling numerical
problems in a different way.

I'm sorry for my general argumentation and lack of practical examples,
but as I had already written, I do program just for fun.

> [...]

And talking about features offered by a programming language, there is
just another addition that could help developing large applications or
bigger projects, and that is 2d and GUI facilities.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0267r10.pdf

Thank you again for the opportunity of this discussion.

alessandro


alessandro volturno

unread,
Sep 17, 2021, 3:56:16 AMSep 17
to
if you read the two lines right before the one here reported you can see
I did that.

I wrote that function to give an extensive description of a rational
number in a way like this:

"4/16 reduces to 1/4 and evaluates to 0.25"

thank you,

alessandro

David Brown

unread,
Sep 17, 2021, 4:35:16 AMSep 17
to
Python might not be much better than Matlab as a language for the
numerical bits (I haven't done Matlab programming to compare), but as a
general purpose language it is vastly more powerful for everything else
than highly specialised and consequently limited tools like Matlab. All
the extra capabilities from Python, such sending emails when your
calculations are done, interacting with databases, generating pdf
reports from the results, etc., are going to make it a lot more
attractive if it can handle the maths you need.


David Brown

unread,
Sep 17, 2021, 5:16:07 AMSep 17
to
On 17/09/2021 09:50, alessandro volturno wrote:
> Il 16/09/2021 22:12, Paavo Helde ha scritto:
>> 16.09.2021 19:24 alessandro volturno kirjutas:
>>> Il 16/09/2021 15:40, Paavo Helde ha scritto:
>
>>>> What are the use cases for rationals? If something is not in the
>>>> standard, then often there are different use cases which cannot be
>>>> easily covered by a common "standard" implementation.
>
>> I notice that you still haven't presented any use case for rationals.
>> I have to admit I also wrote a C++ class for rational numbers ca 20
>> years ago, but so far I have not found any usage for it in my work
>> (which also involves a lot of heavy numeric computations).
>
> I am not a mathematician nor a physicist but probably solving systems of
> linear equations with Gauss or Gauss-Jordan methods can be an
> application of rational numbers.
>

You will quickly get meaninglessly big numbers if you use rationals for
that kind of thing. Calculations with rationals usually only makes
sense for pure mathematics and number theory, not applied mathematics or
physics.

> Many years ago trying to use the Common lisp programming language, I was
> impressed by its natural handling of rational numbers and by its use of
> integers or floating point numbers of arbitrary precision. Common Lisp
> is an ANSI standard dated 1990 but it inherits properties and behaviours
> from ancient LISP dialects. So it seemed to me a bit strange that a
> programming language as widespread as C++ doesn't offer that same
> facilities.
>

C++ arithmetic aims for efficiency, predictability, and fixed sizes.
Fixed size rationals are of quite limited use - you can't do much
arithmetic on them before the sizes overflow. As I mentioned earlier,
there is a lot of fun and learning from making classes to support these,
but little practical use - therefore no point in having them in the
standard library. (The C++ standard library has support for
compile-time rational arithmetic, mainly as a convenient way to handle
magnitudes of SI units.)

So for useful rationals, you need arbitrary precision integers. And
that is a whole different ballgame from fixed sizes - you are now
talking about memory management, big complicated algorithms, and all
sorts of trade-offs in the implementation. For some languages, it's
okay to pick one "reasonable" implementation. It might be big and
incorporate a range of algorithms for different sizes - that's fine for
a language like Python that already has huge libraries. It might be
small and simple, and do a reasonable job for smaller sizes but be less
optimal for huge values - that made sense for Bart in his language.

For C++, it's a /lot/ harder to decide what to do for the standard
library, as C++ programmers have such different needs. Someone who just
wants to do arithmetic up to 4K bits for cryptography will not want the
cost to support megabit sizes. Someone who needs a lot of decimal I/O
might want a base-10 model rather than a base-2 model. People with
particular processors might want a model optimised for the SIMD
instructions they have, though it might be much less efficient on other
processors.

C++ does not try to put /everything/ a programmer might need into its
standard library - it aims to have the tools and basics there, so that
others can make libraries as needed. That's the case here.

>> The only advantage what rationals offer above floating-point is that
>> the calculation results are always exact. However, in numeric
>> programming the input data often comes from a physical measurement,
>> meaning that it already contains measurement inaccuracies. Thus the
>> end result will not be absolutely accurate anyway, so there is no need
>> to use absolutely precise computations.
>
> there are not only physical measures, you could develop examples with
> integer numbers just for didactic goals. C++ must be learned like any
> other discipline. And having wide numerical facilities can be very
> useful permitting to develop new strategies by tackling numerical
> problems in a different way.
>

I agree with those aims. And a C++ standard library for rational
numbers would be completely against that aim - how could you learn about
making good classes and abstractions using rational numbers if the
library already supported them? Make it yourself - that's how you will
learn.

> I'm sorry for my general argumentation and lack of practical examples,
> but as I had already written, I do program just for fun.
>
>> [...]
>
> And talking about features offered by a programming language, there is
> just another addition that could help developing large applications or
> bigger projects, and that is 2d and GUI facilities.
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0267r10.pdf
>

The proposal for adding gui facilities to the C++ standard is /highly/
controversial. Some people think it would be nice to have in the
standard because "everyone" needs graphics and a gui. Others think it
is a terrible idea because there are several dozen popular gui
libraries, with wildly varying pros and cons, and making a "standard C++
gui library" would be as bad an idea as a country's roads department
picking a standard car.

Ian Collins

unread,
Sep 17, 2021, 6:07:32 AMSep 17
to
Matlab does have one advantage (at least in our environment); it can
convert models into C++ code!

--
Ian.

Juha Nieminen

unread,
Sep 17, 2021, 7:16:34 AMSep 17
to
I still think that designwise such a function does not belong in that class.

Sure, nobody is stopping you from adding such functions to your class, if
and when you want to use just in your own small projects, but generally,
when designing such classes for general use for a wider audience and a
wider set of applications, that's just not something that logically
belongs to such a class.

A class that behaves like a numerical value shouldn't itself print
anything, because that doesn't make much logical sense. You could have
separate functions that do that, but they don't belong as members of
the class.

Even when you do implement something like an operator<<(std::ostream&)
for the class, its output should just be an ascii representation of the
number itself, and nothing else.

If you want to provide such printing functions for convenience, you could
implement them as separate functions (maybe even declared in their own
separate header file). Note, however, that you will be fixing the
format (and language) of such messages, which is one of the reasons
why it's not something you usually want to do. Let the user of the
class decide how such things are printed, rather than the library
forcing a particular format (and language). (After all, it's not such
a huge amount of work to write a simple printing line which uses
values from the instance of the class.)

David Brown

unread,
Sep 17, 2021, 7:32:56 AMSep 17
to
A few times in the past, I've seen C code generated by Matlab, produced
by researchers who then wanted the whole think running in a little
microcontroller. The generated code was utterly hideous - vastly
over-complex, and ridiculous inefficiencies (malloc allocation for
things that should be local stack variables, multiplying integers by 0.5
instead of dividing by 2, using strings and strcmp() instead of enums,
etc.). But perhaps it was the user rather than the software that was at
fault - it's not a tool I use myself. I just know that for use on a
small microcontroller, I'd rather hand-translate some well-written
Python than clear up the vomit that Matlab produced.

Christian Gollwitzer

unread,
Sep 17, 2021, 9:55:42 AMSep 17
to
Am 17.09.21 um 10:34 schrieb David Brown:
> On 17/09/2021 09:21, Christian Gollwitzer wrote:
>>
>> Am 16.09.21 um 19:05 schrieb Scott Lurndal:
>>> I suspect that the vast majority of people use existing
>>> software packages like Matlab or R (for statistics) than write C++ code
>>> for numerical software nowadays.  It's really hard to get it
>>> right when using binary floating point, hence standard numerical
>>> libraries (LINPACK, et alia).
>>
>> I am a scientist, and Matlab and R are popular, but slowly they give way
>> to Python. With the "numpy" package wrapping LAPACK and "scipy", which
>> is based on it, Python code almost looks like Matlab, but it's
>> completely open source. To my understanding, that and the machine
>> learning libs are the reasons why Python gained so much in the last
>> years. The language on its own is not that much better than other modern
>> scripting languages, it's the libraries with a large user base.
>>
>
> Python might not be much better than Matlab as a language for the
> numerical bits (I haven't done Matlab programming to compare), but as a
> general purpose language it is vastly more powerful for everything else
> than highly specialised and consequently limited tools like Matlab.

I agree, and maybe I wasn't clearly expressing myself. Matlab has a
little edge if one does linear algebra stuff. There are some weird extra
parentheses in numpy sometimes (e.g. numpy.zeros((3,4)) vs zeros(3,4) in
Matlab), numpy does not distinguish row and column vectors and therefore
leads to more cumbersome code for matrix multiplications, and literals
also look cleaner in Matlab. In addition, the documentation of Matlab
and the toolboxes is really high standard, commercial quality and well
maintained whereas in Scipy you can often see that it is more of a
"hobbyist" thing.

But these are minor disadvantages of Python that are a bit annoying in
interactive use like Jupyter notebooks and not real show-stoppers.

> All
> the extra capabilities from Python, such sending emails when your
> calculations are done, interacting with databases, generating pdf
> reports from the results, etc., are going to make it a lot more
> attractive if it can handle the maths you need.


OTOH, Matlab is a terrible general purpose language. They have bolted on
to that matrix language everything, you *can* use object orientation,
general I/O, GUIs and also send emails
https://www.mathworks.com/help/matlab/import_export/sending-email.html
connect databases https://www.mathworks.com/help/database/ug/odbc.html
and create PDFs
https://www.mathworks.com/help/rptgen/ug/create-an-html-or-pdf-template.html
but it often looks quirky and not like a language I would want to write
a larger program in.

Python was constructed as a sane language to begin with, but so have
been Lua, Julia, Ruby, Kotlin, and surely dozens of others. Why Python
has become the Nr#1? I suspect that as soon as scientists picked it up
with numpy it grew as an alternative to the de-facto standard Matlab
simply because it was free. And then the machine learning guys really
boosted it to a point where it couldn't be ignored any longer.

The same happened years ago with Tcl. Python and Tcl are almost the same
age. Due to Tk, which allowed non-guru-level programmers to make simple
GUIs and the adoption of the circuit CAD industry, Tcl became the
de-facto scripting standard for many years. The decline came with the
stall of development (Sun pulled the money out) and the main focus has
shifted away from desktop GUI and circuit board designers. Now it's
Python that everyone uses and recommends.

Christian




Scott Lurndal

unread,
Sep 17, 2021, 10:03:19 AMSep 17
to
I could rant for hours on the unsuitability of the silly
C++ output stream crap in real applications.

But I've got too much on my plate right now. Much of which
is making performance improvements to a large CPU-bound
C++ application; primarily by getting rid of all outputstringstream
crap (replacing with snprintf) and eliminating most trivial
run-time (vs. startup time) uses of std::string.

void
a(void)
{
std::string fred = "this is a test";
printf("%s", fred.c_str());
}

0000000000400970 <a()>:
400970: 53 push %rbx
400971: be 50 0b 40 00 mov $0x400b50,%esi
400976: 48 83 ec 20 sub $0x20,%rsp
40097a: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
40097f: 48 8d 54 24 0f lea 0xf(%rsp),%rdx
400984: e8 97 fe ff ff callq 400820 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)@plt>
400989: 48 8b 74 24 10 mov 0x10(%rsp),%rsi
40098e: bf 5f 0b 40 00 mov $0x400b5f,%edi
400993: 31 c0 xor %eax,%eax
400995: e8 36 fe ff ff callq 4007d0 <printf@plt>
40099a: 48 8b 44 24 10 mov 0x10(%rsp),%rax
40099f: 48 8d 78 e8 lea -0x18(%rax),%rdi
4009a3: 48 81 ff 80 10 60 00 cmp $0x601080,%rdi
4009aa: 75 06 jne 4009b2 <a()+0x42>
4009ac: 48 83 c4 20 add $0x20,%rsp
4009b0: 5b pop %rbx
4009b1: c3 retq
4009b2: b9 00 00 00 00 mov $0x0,%ecx
4009b7: 48 8d 57 10 lea 0x10(%rdi),%rdx
4009bb: 48 85 c9 test %rcx,%rcx
4009be: 74 35 je 4009f5 <a()+0x85>
4009c0: 83 c8 ff or $0xffffffff,%eax
4009c3: f0 0f c1 02 lock xadd %eax,(%rdx)
4009c7: 85 c0 test %eax,%eax
4009c9: 7f e1 jg 4009ac <a()+0x3c>
4009cb: 48 8d 74 24 0f lea 0xf(%rsp),%rsi
4009d0: e8 3b fe ff ff callq 400810 <std::string::_Rep::_M_destroy(std::allocator<char> const&)@plt>
4009d5: eb d5 jmp 4009ac <a()+0x3c>
4009d7: 48 89 c3 mov %rax,%rbx
4009da: 48 8b 44 24 10 mov 0x10(%rsp),%rax
4009df: 48 8d 74 24 0f lea 0xf(%rsp),%rsi
4009e4: 48 8d 78 e8 lea -0x18(%rax),%rdi
4009e8: e8 03 fe ff ff callq 4007f0 <std::string::_Rep::_M_dispose(std::allocator<char> const&)@plt>
4009ed: 48 89 df mov %rbx,%rdi
4009f0: e8 5b fe ff ff callq 400850 <_Unwind_Resume@plt>
4009f5: 8b 50 f8 mov -0x8(%rax),%edx
4009f8: 8d 4a ff lea -0x1(%rdx),%ecx
4009fb: 89 48 f8 mov %ecx,-0x8(%rax)
4009fe: 89 d0 mov %edx,%eax
400a00: eb c5 jmp 4009c7 <a()+0x57>
400a02: 66 66 66 66 66 2e 0f data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
400a09: 1f 84 00 00 00 00 00

(and that's _with_ -O3).

Scott Lurndal

unread,
Sep 17, 2021, 10:07:00 AMSep 17
to
One of my colleagues wrote a tool (verilator) to convert Verilog
into C++ code. Quite useful.

https://en.wikipedia.org/wiki/Verilator

Christian Gollwitzer

unread,
Sep 17, 2021, 11:13:21 AMSep 17
to
Am 17.09.21 um 16:03 schrieb Scott Lurndal:
> Juha Nieminen <nos...@thanks.invalid> writes:
>> alessandro volturno <alessandr...@libero.it> wrote:
>>> printDescription() // prints a description of the number and its
>>> // numerical value
>>
>> This is not really something that belongs to a class that behaves like an
>> arithmetic numerical value.
>>
>> At most what you could have is a separate
>>
>> std::ostream& operator<<(std::ostream&, YourRationalClass);
>>
>> function for outputting the value to a std::ostream.
>
> I could rant for hours on the unsuitability of the silly
> C++ output stream crap in real applications.
>
> But I've got too much on my plate right now. Much of which
> is making performance improvements to a large CPU-bound
> C++ application; primarily by getting rid of all outputstringstream
> crap (replacing with snprintf) and eliminating most trivial
> run-time (vs. startup time) uses of std::string.
>
> void
> a(void)
> {
> std::string fred = "this is a test";
> printf("%s", fred.c_str());
> }
>
> [...long assembly...]

Wow, thats an awful lot of code. Does it improve if you do const
std::string or constexpr or the like?

Christian

Christian Gollwitzer

unread,
Sep 17, 2021, 11:17:23 AMSep 17
to
Am 17.09.21 um 17:12 schrieb Christian Gollwitzer:
Quick test on compiler explorer shows a much more reasonable code:
https://godbolt.org/z/5MzscK95W

with gcc 11.

Christian

David Brown

unread,
Sep 17, 2021, 11:50:15 AMSep 17
to
Lua, Julia, Ruby, etc., are also free. But Python was there first, and
I think momentum has a lot to do with it. It's also a higher level
language than, say, Lua, and while there are some mixed opinions on some
aspects of Python's syntax, it is generally regarded as a easier to
read, write and learn than Ruby and many alternatives.

> The same happened years ago with Tcl. Python and Tcl are almost the same
> age. Due to Tk, which allowed non-guru-level programmers to make simple
> GUIs and the adoption of the circuit CAD industry, Tcl became the
> de-facto scripting standard for many years. The decline came with the
> stall of development (Sun pulled the money out) and the main focus has
> shifted away from desktop GUI and circuit board designers. Now it's
> Python that everyone uses and recommends.
>

Tcl is okay as a "tool control language", but it is far too weak for
more advanced work. Python was considered too "heavy" for simple
scripting in many areas - but as everything else (disks, memory, cpus,
etc.) has got bigger, the relative cost of Python has dropped.

Christian Gollwitzer

unread,
Sep 17, 2021, 12:23:06 PMSep 17
to
Am 17.09.21 um 17:49 schrieb David Brown:
> On 17/09/2021 15:55, Christian Gollwitzer wrote:
> Tcl is okay as a "tool control language", but it is far too weak for
> more advanced work.

To be fair, one must say "has been". Tcl has improved since the days
back then and acquired many modern things like coroutines, built-in
object orientation etc. but the development went on a very slow path
after the support from Sun was dropped. I'm one of the inhabitants of a
small Gallic village ;) this is one of my projects realized in modern Tcl:

https://github.com/BessyHDFViewer/BessyHDFViewer


But the world has moved on, and now I'm recommending Pyton to everyone
who needs a programming language as the first pick. C++ comes second, if
speed is required or special hardware control etc. Of course, as a
scientist my environment is data analysis and lab hardware control. On
embedded devices the ranking would be different.

Christian

alessandro volturno

unread,
Sep 17, 2021, 12:34:28 PMSep 17
to
Il 17/09/2021 13:16, Juha Nieminen ha scritto:

> I still think that designwise such a function does not belong in that class.

You are right, but the project started as a game and it is not
intentended to leave my PC :-)

> Sure, nobody is stopping you from adding such functions to your class, if
> and when you want to use just in your own small projects, but generally,
> when designing such classes for general use for a wider audience and a
> wider set of applications, that's just not something that logically
> belongs to such a class.
>
> A class that behaves like a numerical value shouldn't itself print
> anything, because that doesn't make much logical sense. You could have
> separate functions that do that, but they don't belong as members of
> the class.

you were perfectly clear, thank you for that.

alessandro

Manfred

unread,
Sep 17, 2021, 1:00:12 PMSep 17
to
Still,

Feeding a C string to a std::string to be fed to printf() /is/ masochism
(or sadism, depending on which side you are on).

Scott Lurndal

unread,
Sep 17, 2021, 1:14:05 PMSep 17
to
One quite often runs across C++ purists (or new grads) who falsly eschew
C constructs as "not C++".

Unfortunately, we need to support GCC4 through GCC11 efficiently.

alessandro volturno

unread,
Sep 17, 2021, 1:20:30 PMSep 17
to
Il 17/09/2021 11:15, David Brown ha scritto:

> You will quickly get meaninglessly big numbers if you use rationals for
> that kind of thing. Calculations with rationals usually only makes
> sense for pure mathematics and number theory, not applied mathematics or
> physics.

A tool is crafted for a certain scope, you could but don't want to peel
an apple with a cutter. If a tool is present in a computer language's
library that doesn't mean every one has ever to use it.

> C++ arithmetic aims for efficiency, predictability, and fixed sizes.
> Fixed size rationals are of quite limited use - you can't do much
> arithmetic on them before the sizes overflow.

This is my personal opinion: C's and C++'s search for efficiency is
probably one of the cause that made other computer scientists develop
newer or more complete and easy to use computer languages.

> For C++, it's a /lot/ harder to decide what to do for the standard
> library, as C++ programmers have such different needs. Someone who just
> wants to do arithmetic up to 4K bits for cryptography will not want the
> cost to support megabit sizes. Someone who needs a lot of decimal I/O
> might want a base-10 model rather than a base-2 model. People with
> particular processors might want a model optimised for the SIMD
> instructions they have, though it might be much less efficient on other
> processors.

If something is in the standard library that doesn't mean everyone has
to use it. Your argument is valid if it were made a built in facility,
but in Common Lisp you have the usual fixed sized numerical types as
well as the wider and heavier ratios or numbers of arbitrary precision.
it's at your discretion use those things or not.

> C++ does not try to put /everything/ a programmer might need into its
> standard library - it aims to have the tools and basics there, so that
> others can make libraries as needed. That's the case here.

Libraries are a bless, but there are so many of them all around you get
confused, and one has to spend many weeks studying one of them to obtain
something you could do inside your favourite programming language.

I have written, some years ago, a silly game in C++, quite similar to
Amiga's Colors. The platform used to develop it was linux. and well, to
paint on the screen I had to use an external library (Allegro 4).

Another toy program that I wrote uses FLTK's GUI facility. If I had the
opportunity of a graphics library inside C++, my program could just be
recompiled on Windows to work, without having to compile the library
using tools like cygwin to make it working in Windows.

>>> The only advantage what rationals offer above floating-point is that
>>> the calculation results are always exact. However, in numeric
>>> programming the input data often comes from a physical measurement,
>>> meaning that it already contains measurement inaccuracies.

Physics is not the only Science out there

>> [...] you could develop examples with
>> integer numbers just for didactic goals. C++ must be learned like any
>> other discipline. And having wide numerical facilities can be very
>> useful permitting to develop new strategies by tackling numerical
>> problems in a different way.
>>

> I agree with those aims. And a C++ standard library for rational
> numbers would be completely against that aim

Why?

- how could you learn about
> making good classes and abstractions using rational numbers if the
> library already supported them? Make it yourself - that's how you will
> learn.

You could just try to reinvent the wheel, thinking about how it could be
implemented. But that doesn't mean having rationals in the language
hinder you to mimic its functionality.

Thank you,

alessandro

Ian Collins

unread,
Sep 17, 2021, 5:15:41 PMSep 17
to
The generated code isn't great, but with a little care in the model, it
isn't too bad either. I liken the process to the hand optimisations we
used to do in C to get the best from 80's vintage compilers!

--
Ian.

Ian Collins

unread,
Sep 17, 2021, 5:34:43 PMSep 17
to
Maybe a better compiler would help? With clang++ and -O2:

_Z1av: # @_Z1av
.cfi_startproc
# %bb.0:
pushq %rbx
.cfi_def_cfa_offset 16
subq $32, %rsp
.cfi_def_cfa_offset 48
.cfi_offset %rbx, -16
leaq 16(%rsp), %rbx
movq %rbx, (%rsp)
movabsq $2338328219631577204, %rax # imm = 0x2073692073696874
movq %rax, 16(%rsp)
movabsq $8391162080155213939, %rax # imm = 0x7473657420612073
movq %rax, 22(%rsp)
movq $14, 8(%rsp)
movb $0, 30(%rsp)
movl $.L.str.1, %edi
movq %rbx, %rsi
xorl %eax, %eax
callq printf
movq (%rsp), %rdi
cmpq %rbx, %rdi
je .LBB0_2
# %bb.1:
callq _ZdlPv
.LBB0_2:
addq $32, %rsp
.cfi_def_cfa_offset 16
popq %rbx
.cfi_def_cfa_offset 8
retq

--
Ian.

James Kuyper

unread,
Sep 17, 2021, 7:00:45 PMSep 17
to
On 9/17/21 1:19 PM, alessandro volturno wrote:
> Il 17/09/2021 11:15, David Brown ha scritto:
...
>>>> The only advantage what rationals offer above floating-point is that
>>>> the calculation results are always exact. However, in numeric
>>>> programming the input data often comes from a physical measurement,
>>>> meaning that it already contains measurement inaccuracies.
>
> Physics is not the only Science out there

Yes, but exactly rational numbers that are an accurate reflection of
something in reality remain rare in all of the sciences.

David Brown

unread,
Sep 18, 2021, 5:39:36 AMSep 18
to
On 17/09/2021 19:19, alessandro volturno wrote:
> Il 17/09/2021 11:15, David Brown ha scritto:
>

>> C++ arithmetic aims for efficiency, predictability, and fixed sizes.
>> Fixed size rationals are of quite limited use - you can't do much
>> arithmetic on them before the sizes overflow. 
>
> This is my personal opinion: C's and C++'s search for efficiency is
> probably one of the cause that made other computer scientists develop
> newer or more complete and easy to use computer languages.

Yes - and that's a good thing. There are all kinds of programming
tasks, and all kinds of programmers - there needs to be a variety of
programming languages. C++ should not become Python or Lisp any more
than Python should become Lua or Lisp should become Fortran.

>
>>>> The only advantage what rationals offer above floating-point is that
>>>> the calculation results are always exact. However, in numeric
>>>> programming the input data often comes from a physical measurement,
>>>> meaning that it already contains measurement inaccuracies.
>
> Physics is not the only Science out there
>

You were the one that brought up physics ("I am not a mathematician or a
physicist"). /All/ sciences - to be worthy of the name "science" -
involve measurements of real things. Almost always, these are inexact
measurements, with the exceptions being relatively small whole number
counts. Rational numbers turn up very rarely in science of any kind.
Probably the only science in which they /do/ turn up is quantum
mechanics in physics, and even there we are talking about small and
specific values (half-integer spin, third or two-third charges on
quarks, that kind of thing).

Rational arithmetic does not really turn up anywhere outside pure
mathematics and certain direct applications (such as in cryptography).

alessandro volturno

unread,
Sep 18, 2021, 5:56:46 AMSep 18
to
But as you say, can have some advantage from them.
Anyway I now have a clear picture of the scenario about rational numbers
and C++ standard.

I can consider the question closed.

Thank you to all who took part in this thread.

alessandro

David Brown

unread,
Sep 18, 2021, 6:00:30 AMSep 18
to
On 17/09/2021 23:34, Ian Collins wrote:
> On 18/09/2021 02:03, Scott Lurndal wrote:
>> Juha Nieminen <nos...@thanks.invalid> writes:
>>> alessandro volturno <alessandr...@libero.it> wrote:
>>>> printDescription() // prints a description of the number and its
>>>>                     // numerical value
>>>
>>> This is not really something that belongs to a class that behaves
>>> like an
>>> arithmetic numerical value.
>>>
>>> At most what you could have is a separate
>>>
>>>   std::ostream& operator<<(std::ostream&, YourRationalClass);
>>>
>>> function for outputting the value to a std::ostream.
>>
>> I could rant for hours on the unsuitability of the silly
>> C++ output stream crap in real applications.
>>
>> But I've got too much on my plate right now. Much of which
>> is making performance improvements to a large CPU-bound
>> C++ application;  primarily by getting rid of all outputstringstream
>> crap (replacing with snprintf) and eliminating most trivial
>> run-time (vs. startup time) uses of std::string.
>>
>> void
>> a(void)
>> {
>>      std::string fred = "this is a test";
>>      printf("%s", fred.c_str());
>> }
>>

>> (and that's _with_ -O3).
>
> Maybe a better compiler would help?  With clang++ and -O2:
>
Or pretty much any version of gcc with -O2, at least according to my
tests on <https://godbolt.org>

Maybe Scott has unusual options, or an unusual library, or other
surrounding code that affects the results.

There are plenty of reasons to dislike C++ output streams (for me, it is
the moronic design decision of stateful formatting flags that are the
big problem). The quality of code generated for a weird mixture of
C-style and C++-style, for an operation that is always big and slow, is
not such a concern.

Bart

unread,
Sep 18, 2021, 6:02:32 AMSep 18
to
So where does big integer arithemetic turn up?

With big integers, I can see that sometimes you want (A/B)*B to result in A.

With integer divide, that might not be the case.

Using floating point divide with finite precision, you can lose
information (and perversely end up with too much useless precision; if I
do (1/3)*3, with 100M digits, I get 100M digits of 0.9999....).

Letting A/B (perhaps with a special divide op) yield a rational type
would work.

In that case, I can see this being of value with i64 and i128 types too,
for the two parts of a rational number.


Scott Lurndal

unread,
Sep 18, 2021, 10:54:52 AMSep 18
to
David Brown <david...@hesbynett.no> writes:
>On 17/09/2021 23:34, Ian Collins wrote:

>>
>> Maybe a better compiler would help?  With clang++ and -O2:
>>
>Or pretty much any version of gcc with -O2, at least according to my
>tests on <https://godbolt.org>
>
>Maybe Scott has unusual options, or an unusual library, or other
>surrounding code that affects the results.

$ gcc --version
gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

It's what I had on the system I was posting from, and the code
was an illustrative example, not from our proprietary production
code.

>
>There are plenty of reasons to dislike C++ output streams (for me, it is
>the moronic design decision of stateful formatting flags that are the
>big problem).

Indeed, and they're completely unreadable.

> The quality of code generated for a weird mixture of
>C-style and C++-style, for an operation that is always big and slow, is
>not such a concern.

snprintf makes a single pass over the formatting string. It's a single
function call.

output string streams (the "C++" way) has multiple function calls and
generates a shitload of code. And is much less readable and
not as maintainable. The arguments about mismatched format
types is obviated by all modern compilers warning for the *printf
family arguments. Custom classes can use 'to_string' functions
rather than overloading the << operator.

Performance _does_ matter in some applications - ours can eat a
24-core system for lunch, so every cycle matters; especially when
the customer complains about performance (but then our application
simulates a full SoC - sufficient to boot multicore linux and run packet
processing application stacks (e.g DPDK) on the simulator prior
to hardware availability).

Bart

unread,
Sep 18, 2021, 12:37:35 PMSep 18
to
Both approaches are terrible in my opinion:

C++: std::cout << "A=" << a << " B=" << b << " C=" << c << std::endl;

C: printf("A=%d B=%f C=%s\n", a, b, c);

Compared with the equivalent in any of my languages:

M: println =a, =b, =c

The C++ just looks dreadful (and I keep forgetting the << or writing
commas instead).

The C has the big problem of needing to tell the compiler the types of
the expressions you're printing, something it already knows perfectly
well, since it can warn you when they're wrong!

You might not even know yourself, with a complex expression, or one
involving opaque types. And they need maintenance as code changes.

My approach is to have print directly supported by the language. It can
map your code to a series of function calls or, at one time when I
transpiled to C, into a single synthesised printf call.

Or, possibly you can use a feature such as this in my C compiler:

C (bcc): printf("%=? %=? %=?\n", a, b, c);

where it fills in the format codes. Note the the C example above is ONLY
valid when a has an int type, b is double or float, and c is char*,
otherwise those need adjusting. If I reverse the order in my C version:

printf("%=? %=? %=?\n", c, b, a);

It still works fine. If I try the same in the standard C version,
without fixing the formats, it crashes. gcc might warn, /if/ you specify
-Wformat. And then you still need to fix it.


> Performance _does_ matter in some applications - ours can eat a
> 24-core system for lunch, so every cycle matters; especially when
> the customer complains about performance (but then our application
> simulates a full SoC - sufficient to boot multicore linux and run packet
> processing application stacks (e.g DPDK) on the simulator prior
> to hardware availability).

You don't want to make the emulation too good or people won't buy the
hardware...


Christian Gollwitzer

unread,
Sep 18, 2021, 5:30:05 PMSep 18
to
Am 18.09.21 um 18:36 schrieb Bart:
> On 18/09/2021 15:54, Scott Lurndal wrote:
>> snprintf makes a single pass over the formatting string.  It's a single
>> function call.
>>
>> output string streams (the "C++" way) has multiple function calls and
>> generates a shitload of code.  And is much less readable and
>> not as maintainable.   The arguments about mismatched format
>> types is obviated by all modern compilers warning for the *printf
>> family arguments.
>
> Both approaches are terrible in my opinion:
>
> C++:   std::cout << "A=" << a << " B=" << b << " C=" << c << std::endl;
>
> C:     printf("A=%d B=%f C=%s\n", a, b, c);
>
> Compared with the equivalent in any of my languages:
>
> M:     println =a, =b, =c
>
> The C++ just looks dreadful (and I keep forgetting the << or writing
> commas instead).

There is widespread support for this in other languages; e.g. Python3:

Python 3.8.8 (default, Apr 13 2021, 12:59:45)
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a=3;b=4;c='Hallo'
>>> print(f'a={a} b={b} c={c}')
a=3 b=4 c=Hallo
>>>

Tcl:
(base) Apfelkiste:Sources chris$ wish86
% set a 3; set b 4; set c Hallo
Hallo
% puts "a=$a b=$b c=$c"
a=3 b=4 c=Hallo
%


> My approach is to have print directly supported by the language. It can
> map your code to a series of function calls or, at one time when I
> transpiled to C, into a single synthesised printf call.

In the other languages as demonstrated above, it is not a special
"print" function but rather a way to build strings; you can feed it to
any function or assign it to a variable, not just print it. And so it is
much more useful; consider e.g. constructing file names

>>> f'input_{a:03}.png'
'input_003.png'
>>>

...and, surprise:

Christian

Scott Lurndal

unread,
Sep 18, 2021, 6:49:09 PMSep 18
to
Bart <b...@freeuk.com> writes:
>On 18/09/2021 15:54, Scott Lurndal wrote:
>> David Brown <david...@hesbynett.no> writes:
>>> On 17/09/2021 23:34, Ian Collins wrote:

>> snprintf makes a single pass over the formatting string. It's a single
>> function call.

>C: printf("A=%d B=%f C=%s\n", a, b, c);

A trivially useless format string. Try adding field widths and
re-ordering the arguments within the format string for i18n/l10n
purposes.

Ian Collins

unread,
Sep 18, 2021, 7:04:14 PMSep 18
to
On 19/09/2021 02:54, Scott Lurndal wrote:
> David Brown <david...@hesbynett.no> writes:
>> On 17/09/2021 23:34, Ian Collins wrote:
>
>>>
>>> Maybe a better compiler would help?  With clang++ and -O2:
>>>
>> Or pretty much any version of gcc with -O2, at least according to my
>> tests on <https://godbolt.org>
>>
>> Maybe Scott has unusual options, or an unusual library, or other
>> surrounding code that affects the results.
>
> $ gcc --version
> gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)
> Copyright (C) 2013 Free Software Foundation, Inc.
> This is free software; see the source for copying conditions. There is NO
> warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>
> It's what I had on the system I was posting from, and the code
> was an illustrative example, not from our proprietary production
> code.

But you still used it to back up an exaggerated claim regarding the
performance of std::string! If you are using such an old compiler for
production code, you have made a rod for your own back.

>> There are plenty of reasons to dislike C++ output streams (for me, it is
>> the moronic design decision of stateful formatting flags that are the
>> big problem).
>
> Indeed, and they're completely unreadable.

Until you need to stream non-trivial objects, or anything in a template...

>> The quality of code generated for a weird mixture of
>> C-style and C++-style, for an operation that is always big and slow, is
>> not such a concern.
>
> snprintf makes a single pass over the formatting string. It's a single
> function call.
>
> output string streams (the "C++" way) has multiple function calls and
> generates a shitload of code.

This is certainly true.

--
Ian

Bart

unread,
Sep 18, 2021, 7:47:06 PMSep 18
to
You're just picking holes, aren't you?

I don't see the problem with field widths. While internationalisation is
a separate aspect that is not a problem I've ever had in 99.999% of my
uses of printf.

But grappling with the correct format codes has ALWAYS been a problem,
and needs a solution, not nit-picking ideas because you're trying to put
someone down.

(I used a different approach to locale-specific printing decades ago, so
that I would write:

println /"Serial number:", sn

in my code, but at the customer site, it might output:

Serie nummer: 1234

if they spoke Dutch. "/" is a translation operator.)



Bart

unread,
Sep 18, 2021, 8:09:51 PMSep 18
to
Simple Print is one of the most diverse features in program languages
(just take a look through Rosetta Code).

Scripting languages tend to do better, although most seem to want to do
it via functions in libraries, sometimes requiring special features of
the language (in C, it's variadic functions and parameters).

One of the best IMV was BASIC:

PRINT A, B, C ' 1960s and 70s

and I can do the same:

print A, B, C

Although the rules for spacing and newlines still differ widely. My
example will add spacing between the elements. In C++, you need to write:

std::cout << A << " " << B << " " << C;

where you gradually lose the will to live. I assume there is a formatted
Print feature other than C's printf.

(The "=" feature of mine adds a label; invaluable for debugging code,
but elsewhere you usually have to either repeat the expression as a
string, or knock up some C macro to avoid the duplication.)

>
>> My approach is to have print directly supported by the language. It
>> can map your code to a series of function calls or, at one time when I
>> transpiled to C, into a single synthesised printf call.
>
> In the other languages as demonstrated above, it is not a special
> "print" function but rather a way to build strings; you can feed it to
> any function or assign it to a variable, not just print it. And so it is
> much more useful; consider e.g. constructing file names
>
> >>> f'input_{a:03}.png'
> 'input_003.png'
> >>>
>
> ...and, surprise:

If you have string processing anyway, then there are many more
possibilities to getting formatted results. But sticking with formatted
Print, this becomes in my languages:

fprint "input_#.png", a:"z3" # statement-style

s := sfprint("input_#.png", a:"z3") # expression-style

I like the format string to be clean, and free of clutter, so that I can
more easily see what it's supposed to look like!

Christian Gollwitzer

unread,
Sep 19, 2021, 2:06:26 AMSep 19
to
Am 19.09.21 um 01:46 schrieb Bart:
> On 18/09/2021 23:48, Scott Lurndal wrote:
>> Bart <b...@freeuk.com> writes:
>>> On 18/09/2021 15:54, Scott Lurndal wrote:
>>>> David Brown <david...@hesbynett.no> writes:
>>>>> On 17/09/2021 23:34, Ian Collins wrote:
>>
>>>> snprintf makes a single pass over the formatting string.  It's a single
>>>> function call.
>>
>>> C:     printf("A=%d B=%f C=%s\n", a, b, c);
>>
>> A trivially useless format string.  Try adding field widths and
>> re-ordering the arguments within the format string for i18n/l10n
>> purposes.

> that I would write:
>
>     println /"Serial number:", sn
>
> in my code, but at the customer site, it might output:
>
>     Serie nummer: 1234
>
> if they spoke Dutch. "/" is a translation operator.)
>

You didn't get the problem Scott was talking about. If you have more
than 1 variable in a sentence, in the translation the word order might
be changed. E.g.


"The $animal bites $name"

could be tranlsated as

"$name gets bitten by the $animal"

in some language.

Therefore, if you do

printf("The %s bites %s", "dog", "Harry")

and the translator does

"%s gets bitten by the %s"

you will end up with

"dog gets bitten by the Harry"

If, however, there is proper string interpolation like in Python format
strings, then the translator would translatate the string as above, changing

"The {animal} bites {name}"
into
"{name} gets bitten by the {animal}"

and it would come out correctly. Obviously, there are still problems
with inflections; in most languages the dog, Harry etc. are adapted
depending on the function in the sentence (grammatical cases).

Christian

red floyd

unread,
Sep 19, 2021, 2:45:24 AMSep 19
to
On 9/18/2021 5:09 PM, Bart wrote:

> Although the rules for spacing and newlines still differ widely. My
> example will add spacing between the elements. In C++, you need to write:
>
>   std::cout << A << " " << B << " " << C;
>
> where you gradually lose the will to live. I assume there is a formatted
> Print feature other than C's printf.
>

boost::format? Or C++20 std::format?

Bart

unread,
Sep 19, 2021, 5:27:26 AMSep 19
to
But this is not what printf does? That has n$ positional codes, which is
not affected by my suggestion to use, for example, "?" instead of "d",
"f", "s" etc to denote the type of the result.

(And if a format string ends in a list outside the program, it's better
that "?" was in there, than "d" "lld" etc, which now become extra
program code to maintain.)

My point, again, was that this stuff is not the problem with Print in C
and C++ that I was addressing.

(Does C++ have keyword arguments yet? A vastly more useful feature in
everyday coding. If not, then implement those and then we'll talk about
positional print items, which can trivially be dealt with in user-code.)

>
> "The {animal} bites {name}"
> into
> "{name} gets bitten by the {animal}"
>

That seems a reasonable way of doing that, except that the second line
will be translated into the target language; you need to ensure that
'name' and 'animal', identifiers in the source code, are not translated too!

(I would still prefer that those names, which are really expressions,
were outside the string, with a positional scheme like printf's applied
if necessary.

Being expressions, they can presumably include embedded format strings too?)

Anyway, in many applications I've seen, people don't really bother
getting things right even in English: in Windows I often see "1 Files"
being shown (now improved to "1 File(s)"!), when the program /knows/ the
quantity and can easy display either "1 File" or "5 Files".

This is an example of my code from last century (here using string
processing not formatted print):

smcmd((nfiles=0|/"No Files"|(nfiles=1|"1 " + /"File"|str(nfiles) +
/"Files")))

It shows 'No files' or '1 File' or 'N Files'. In Dutch, it would show
'Geen bestanden' or '1 Bestand' or 'N Bestanden'. (I supported German
and French too.)

I can't remember the details, but if there were differences in word
order, then the whole phrase was translated when there were no variable
parts.

Juha Nieminen

unread,
Sep 19, 2021, 6:12:26 AMSep 19
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> But I've got too much on my plate right now. Much of which
> is making performance improvements to a large CPU-bound
> C++ application; primarily by getting rid of all outputstringstream
> crap (replacing with snprintf) and eliminating most trivial
> run-time (vs. startup time) uses of std::string.

While not super rare, it's nevertheless relatively uncommon to need maximum
I/O throughput in most applications that, for example, use this kind of
class as discussed in this thread. In the vast majority of situations,
even in very CPU-intensive number-crunching applications, the I/O speed
is largely irrelevant (because the amount of data to be printed isn't
that great, nor requires maximum speed).

Even in the cases where one *does* need maximum efficiency in I/O
(this would often be some kind of program that handles enormous
amounts of input and/or output data, eg. in some kind of server
or other similar application, requiring the program to read and/or
write gigabytes and gigabytes of data as fast as possible), any
experienced C++ programmer will know that std::ostream and
dynamically allocated strings will not be appropriate for this,
and will use something else. However, even in this case it's not
like having the operator<<() there is going to hurt something.
If it's not suitable for the task at hand, simply don't use it.

There are some situations where having a suitable operator<<()
overload for a particular type is very practical and handy.
Such as, for example, when using Google Test (which has extensive
support for adding additional information to an error message
via operator<< overloads.)

Juha Nieminen

unread,
Sep 19, 2021, 6:17:52 AMSep 19
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> One quite often runs across C++ purists (or new grads) who falsly eschew
> C constructs as "not C++".

My response to them is that "if it's in the C++ standard, then it's C++,
through and through. Use the tools that are best for the task at hand."

Just because something is "inherited" from C (so to speak) doesn't mean
it's not suitable and perfectly valid to use in C++. After all, keywords
like 'for' and 'if' are inherited from C. Does that mean they shouldn't
be used in C++? Why is using those ok, but eg. using std::printf() is not?
What's the difference?

> Unfortunately, we need to support GCC4 through GCC11 efficiently.

I find it fascinating how common gcc 4 is still out there in the wild,
even to this day.

It kind of has taken the mantle of gcc 2, which likewise was in very
wide use years and years after it had become completely obsolete and
antiquated (as, IIRC, it didn't even support 100% of C++98.)
The difference is that gcc 4 has persisted for a *lot* longer than
gcc 2 did.

I blame certain Linux distros for this.

Juha Nieminen

unread,
Sep 19, 2021, 6:23:23 AMSep 19
to
Bart <b...@freeuk.com> wrote:
> Both approaches are terrible in my opinion:
>
> C++: std::cout << "A=" << a << " B=" << b << " C=" << c << std::endl;
>
> C: printf("A=%d B=%f C=%s\n", a, b, c);
>
> Compared with the equivalent in any of my languages:
>
> M: println =a, =b, =c

Uh... println in your languages will automatically print "name=" before
printing the value of a variable? What if you want to use spaces around
the '='? What if you want to use another character instead, like ':',
and have a space only after that character but not before it?

Anyway, it's relatively easy in C++ to implement a function that behaves
like std::ostream, but uses a function call syntax instead, like:

myprint("a=", a, ", b=", b, ", c=", c, "\n");

> The C++ just looks dreadful (and I keep forgetting the << or writing
> commas instead).

It's C++'s fault that you keep forgetting the <<?

Bart

unread,
Sep 19, 2021, 7:04:41 AMSep 19
to
On 19/09/2021 11:23, Juha Nieminen wrote:
> Bart <b...@freeuk.com> wrote:
>> Both approaches are terrible in my opinion:
>>
>> C++: std::cout << "A=" << a << " B=" << b << " C=" << c << std::endl;
>>
>> C: printf("A=%d B=%f C=%s\n", a, b, c);
>>
>> Compared with the equivalent in any of my languages:
>>
>> M: println =a, =b, =c
>
> Uh... println in your languages will automatically print "name=" before
> printing the value of a variable? What if you want to use spaces around
> the '='? What if you want to use another character instead, like ':',
> and have a space only after that character but not before it?

It prints the whole expression not just the name, and is primarily for
debugging prints where large numbers of such temporary statements will
be added and removed. With C, it would make my RSI worse.

C allows you to define a macro to reduce that duplication, example:

#define EQ(x) #x "=",x

where the expression is an exact copy of what's in the source (although
I prefer them in upper case for emphasis). But if I plug that into my C
example:

printf("%s%d %s%f %s%s\n", EQ(a), EQ(b), EQ(c));

you find you're doing even more typing!

Of course for permanent print statements and more precise control, you
add those annotations more conventionally.

> Anyway, it's relatively easy in C++ to implement a function that behaves
> like std::ostream, but uses a function call syntax instead, like:
>
> myprint("a=", a, ", b=", b, ", c=", c, "\n");
>
>> The C++ just looks dreadful (and I keep forgetting the << or writing
>> commas instead).
>
> It's C++'s fault that you keep forgetting the <<?
>

Yes, because it is so peculiar. I wouldn't know how to create such a
function, but it would have been a better way of presenting a print
feature, and more conventional.

Scott Lurndal

unread,
Sep 19, 2021, 10:08:05 AMSep 19
to
Juha Nieminen <nos...@thanks.invalid> writes:
>Scott Lurndal <sc...@slp53.sl.home> wrote:
>> One quite often runs across C++ purists (or new grads) who falsly eschew
>> C constructs as "not C++".
>
>My response to them is that "if it's in the C++ standard, then it's C++,
>through and through. Use the tools that are best for the task at hand."
>
>Just because something is "inherited" from C (so to speak) doesn't mean
>it's not suitable and perfectly valid to use in C++. After all, keywords
>like 'for' and 'if' are inherited from C. Does that mean they shouldn't
>be used in C++? Why is using those ok, but eg. using std::printf() is not?
>What's the difference?
>
>> Unfortunately, we need to support GCC4 through GCC11 efficiently.
>
>I find it fascinating how common gcc 4 is still out there in the wild,
>even to this day.

It is the default compiler for Redhat 6 and Redhat 7 (and thus
the deriviations such as CentOS) which are widely used.

>
>I blame certain Linux distros for this.

I wouldn't use the verb "blame" here. Most programmers don't
particularly care about the version of the compiler, so long as
it works.

Juha Nieminen

unread,
Sep 19, 2021, 11:58:18 AMSep 19
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
>>I blame certain Linux distros for this.
>
> I wouldn't use the verb "blame" here. Most programmers don't
> particularly care about the version of the compiler, so long as
> it works.

Yeah, the problem with gcc 4 is that it doesn't support fully C++11
(if I remember correctly), much less newer versions.

Juha Nieminen

unread,
Sep 19, 2021, 12:01:41 PMSep 19
to
Bart <b...@freeuk.com> wrote:
> Yes, because it is so peculiar. I wouldn't know how to create such a
> function, but it would have been a better way of presenting a print
> feature, and more conventional.

The basic idea with overloading a binary operator, rather than using a
function call syntax, is that the output can be expanded with your own
custom types (which is not really possible if it used a function call
syntax).

In other words, you can achieve this:

MyClass obj;
std::cout << "Value = " << obj << "\n";

I suppose there could be a contrived way of achieving the same thing
with a function call syntax, ie. that you could write

std::cout("Value = ", obj, "\n");

but I'm not sure how simple that could be made to be. Especially in C++98.

Bart

unread,
Sep 19, 2021, 12:40:07 PMSep 19
to
That doesn't make sense to me, or maybe there are some limitations in
C++ so that it can only work that way.

I don't do overloads in my languages except for the 'tostr' operator
(normally unary, but see below) in my dynamic language.

'tostr' is applied automatically to each item in a statement like this:

println a, b, c

And it turns whatever a, b, c are into strings.

There is a default handler for the types known to the language, but a
user defined handler can be applied to a user type.

Example (the overloading syntax is crude, but it works):

record date=(var day,month,year)

function tostr_date(a,fmt)=
return sfprint("#/#/# CE", a.day, a.month, a.year)
end

d:=date(19,9,2021)

println d # default tostr shows '(19,9,2021)'

$setoverload(($tostr),date,tostr_date)

println d # custom tostr shows '19/9/2021 CE'


No binary overloads of some mysterious "<<" operator needed, although
'tostr' is really a binary operator; the second operand provides
optional format info, ignored in my example. If I write:

println d:"..."

then that "..." string appears as the fmt parameter, and it can be used
in any manner.

Scott Lurndal

unread,
Sep 19, 2021, 5:53:50 PMSep 19
to
That is correct. As the worldwide data centers migrate to newer RHEL
releases, we're planning on GCC 7.3 as the baseline, which should
open up _some_ limited C++11 feature use (e.g. static_assert would be
useful to elimate some unnecessary runtime assertion checks).

Scott Lurndal

unread,
Sep 19, 2021, 5:54:35 PMSep 19
to
Juha Nieminen <nos...@thanks.invalid> writes:
>Bart <b...@freeuk.com> wrote:
>> Yes, because it is so peculiar. I wouldn't know how to create such a
>> function, but it would have been a better way of presenting a print
>> feature, and more conventional.
>
>The basic idea with overloading a binary operator, rather than using a
>function call syntax, is that the output can be expanded with your own
>custom types (which is not really possible if it used a function call
>syntax).
>
>In other words, you can achieve this:
>
> MyClass obj;
> std::cout << "Value = " << obj << "\n";

fprintf(stdout, "Value = %s\n" obj.to_string());