Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Simple 2d C99 fractal plotter...

131 views
Skip to first unread message

Chris M. Thomasson

unread,
Jan 25, 2019, 5:25:05 PM1/25/19
to
Just wondering, when you get some free time, if you can perhaps run the
my experimental plotter code that generates a PPM called "ct_plane.ppm".
And if you can see a fractal in the resulting rendering. Here is my C99
code:

https://pastebin.com/raw/322XAnsT
(raw text, pure C99: no ads!)

It should just compile with GCC, and run like a charm. It has proper
aspect ratios so one can use any dimensions they want.

Can you notice anything wrong with the code below:

Btw, Hugh is one of my fractal friends... :^)
___________________________
/*
A Simple 2d Plane For Hugh, with proper aspect ratios!
By: Chris M. Thomasson
*_____________________________________________________________*/


#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <complex.h>
#include <tgmath.h>
#include <stdbool.h>


#define CT_WIDTH 1920
#define CT_HEIGHT 1080
#define CT_N 5000000


struct ct_canvas
{
unsigned long width;
unsigned long height;
unsigned char* buf;
};

bool
ct_canvas_create(
struct ct_canvas* const self,
unsigned long width,
unsigned long height
){
size_t size = width * height;

self->buf = calloc(1, size);

if (self->buf)
{
self->width = width;
self->height = height;

return true;
}

return false;
}

void
ct_canvas_destroy(
struct ct_canvas const* const self
){
free(self->buf);
}

bool
ct_canvas_save_ppm(
struct ct_canvas const* const self,
char const* fname
){
FILE* fout = fopen(fname, "w");

if (fout)
{
char const ppm_head[] =
"P3\n"
"# Chris M. Thomasson Simple 2d Plane ver:0.0.0.0 (pre-alpha)";

fprintf(fout, "%s\n%lu %lu\n%u\n",
ppm_head,
self->width, self->height,
255U);

size_t size = self->width * self->height;

for (size_t i = 0; i < size; ++i)
{
unsigned int c = self->buf[i];
fprintf(fout, "%u %u %u ", c, 0U, 0U);
}

if (! fclose(fout))
{
return true;
}
}

return false;
}




struct ct_axes
{
double xmin;
double xmax;
double ymin;
double ymax;
};

struct ct_axes
ct_axes_from_point(
double complex z,
double radius
){
struct ct_axes axes = {
creal(z) - radius, creal(z) + radius,
cimag(z) - radius, cimag(z) + radius
};

return axes;
}


struct ct_plane
{
struct ct_axes axes;
double xstep;
double ystep;
};


void
ct_plane_init(
struct ct_plane* const self,
struct ct_axes const* axes,
unsigned long width,
unsigned long height
){
self->axes = *axes;

double awidth = self->axes.xmax - self->axes.xmin;
double aheight = self->axes.ymax - self->axes.ymin;

assert(width > 0 && height > 0 && awidth > 0.0);

double daspect = fabs((double)height / width);
double waspect = fabs(aheight / awidth);

if (daspect > waspect)
{
double excess = aheight * (daspect / waspect - 1.0);
self->axes.ymax += excess / 2.0;
self->axes.ymin -= excess / 2.0;
}

else if (daspect < waspect)
{
double excess = awidth * (waspect / daspect - 1.0);
self->axes.xmax += excess / 2.0;
self->axes.xmin -= excess / 2.0;
}

self->xstep = (self->axes.xmax - self->axes.xmin) / width;
self->ystep = (self->axes.ymax - self->axes.ymin) / height;
}



struct ct_plot
{
struct ct_plane plane;
struct ct_canvas* canvas;
};


void
ct_plot_init(
struct ct_plot* const self,
struct ct_axes const* axes,
struct ct_canvas* canvas
){
ct_plane_init(&self->plane, axes, canvas->width - 1, canvas->height
- 1);
self->canvas = canvas;
}


bool
ct_plot_point(
struct ct_plot* const self,
double complex z,
unsigned char color
){
long x = (creal(z) - self->plane.axes.xmin) / self->plane.xstep;
long y = (self->plane.axes.ymax - cimag(z)) / self->plane.ystep;

if (x > -1 && x < (long)self->canvas->width &&
y > -1 && y < (long)self->canvas->height)
{
// Now, we can convert to index.
size_t i = x + y * self->canvas->width;

assert(i < self->canvas->height * self->canvas->width);

self->canvas->buf[i] = color;
return true;
}

return false;
}



// Compute the fractal
void
ct_ifs(
struct ct_plot* const plot,
unsigned long n
){
// 2 sets
double complex jp[] = {
.5 + 0 * I,
-5.5 + I*.0
};

double complex z = 0+0*I;
double complex c = 0+0*I;

for (unsigned long i = 0; i < n; ++i)
{
double rn0 = rand() / (RAND_MAX - .0);
double rn1 = rand() / (RAND_MAX - .0);

if (rn0 > .5)
{
c = jp[0];
}

else
{
c = jp[1];
}


double complex d = z - c;
double complex root = csqrt(d);

z = root;

if (rn1 > .5)
{
z = -root;
}

ct_plot_point(plot, z, 255);
ct_plot_point(plot, root, 255);

if (! (i % 256))
{
printf("rendering: %lu of %lu\r", i + 1, n);
}
}

printf("rendering: %lu of %lu\n", n, n);
}





// slow, so what for now... ;^)
void ct_circle(
struct ct_plot* const plot,
double complex c,
double radius,
unsigned int n
){
double abase = 6.2831853071 / n;

for (unsigned int i = 0; i < n; ++i)
{
double angle = abase * i;

double complex z =
(creal(c) + cos(angle) * radius) +
(cimag(c) + sin(angle) * radius) * I;

ct_plot_point(plot, z, 255);
}
}


int main(void)
{
struct ct_canvas canvas;

if (ct_canvas_create(&canvas, CT_WIDTH, CT_HEIGHT))
{
double complex plane_origin = 0+0*I;
double plane_radius = 2.0;

struct ct_axes axes = ct_axes_from_point(plane_origin,
plane_radius);

struct ct_plot plot;
ct_plot_init(&plot, &axes, &canvas);


ct_ifs(&plot, CT_N);

// Our unit circle
ct_circle(&plot, 0+0*I, 1.0, 2048);
ct_circle(&plot, 2+0*I, 2.0, 2048);
ct_circle(&plot, -2+0*I, 2.0, 2048);
ct_circle(&plot, 0+2*I, 2.0, 2048);
ct_circle(&plot, 0-2*I, 2.0, 2048);

ct_canvas_save_ppm(&canvas, "ct_plane.ppm");

ct_canvas_destroy(&canvas);

return EXIT_SUCCESS;
}

return EXIT_FAILURE;
}
___________________________

Chris M. Thomasson

unread,
Jan 25, 2019, 6:11:14 PM1/25/19
to
On 1/25/2019 2:24 PM, Chris M. Thomasson wrote:
> Just wondering, when you get some free time, if you can perhaps run the
> my experimental plotter code that generates a PPM called "ct_plane.ppm".
> And if you can see a fractal in the resulting rendering. Here is my C99
> code:
>
> https://pastebin.com/raw/322XAnsT
> (raw text, pure C99: no ads!)
>
> It should just compile with GCC, and run like a charm. It has proper
> aspect ratios so one can use any dimensions they want.
[...]

It should create a PPM called ct_plane.ppm that look just like this
rendering:

https://plus.google.com/101799841244447089430/posts/PQ6jekiPpqo

Eli the Bearded

unread,
Jan 25, 2019, 6:50:28 PM1/25/19
to
In comp.lang.c, Chris M. Thomasson <ahh_...@crap.nothing> wrote:
> Just wondering, when you get some free time, if you can perhaps run the
> my experimental plotter code that generates a PPM called "ct_plane.ppm".
> And if you can see a fractal in the resulting rendering. Here is my C99
> code:
>
> https://pastebin.com/raw/322XAnsT
> (raw text, pure C99: no ads!)
>
> It should just compile with GCC, and run like a charm. It has proper
> aspect ratios so one can use any dimensions they want.
>
> Can you notice anything wrong with the code below:
...
> if (fout)
> {
> char const ppm_head[] =
> "P3\n"
> "# Chris M. Thomasson Simple 2d Plane ver:0.0.0.0 (pre-alpha)";
>
> fprintf(fout, "%s\n%lu %lu\n%u\n",
> ppm_head,
> self->width, self->height,
> 255U);
>
> size_t size = self->width * self->height;
>
> for (size_t i = 0; i < size; ++i)
> {
> unsigned int c = self->buf[i];
> fprintf(fout, "%u %u %u ", c, 0U, 0U);
> }

If you are going to have monochrome output (all red channel here), why
not just use the PGM format ("P2")? And since you are using just 0..255,
you can very easily use a raw format ("P5" for pgm / "P6" for ppm) and
create a much smaller file.

$ pnmtopnm ct_plane.ppm > noplain.ppm
$ ls -l ct_plane.ppm noplain.ppm
-rw-rw-r--. 1 user group 14584534 Jan 25 15:08 ct_plane.ppm
-rw-rw-r--. 1 user group 6220817 Jan 25 15:16 noplain.ppm
$ ppmtorgb3 ct_plane.ppm
$ ls -l ct_plane.red
-rw-rw-r--. 1 user group 2073617 Jan 25 15:19 ct_plane.red
$ file noplain.ppm ct_plane.red
noplain.ppm: Netpbm PPM "rawbits" image data, size = 1920 x 1080
ct_plane.red: Netpbm PGM "rawbits" image data, size = 1920 x 1080
$ diff fractal.c.orig fractal.c
56c56
< ct_canvas_save_ppm(
---
> ct_canvas_save_pgm(
64,65c64,65
< char const ppm_head[] =
< "P3\n"
---
> char const pgm_head[] =
> "P5\n"
69c69
< ppm_head,
---
> pgm_head,
78c78
< fprintf(fout, "%u %u %u ", c, 0U, 0U);
---
> putc(c, fout);
307c307
< ct_canvas_save_ppm(&canvas, "ct_plane.ppm");
---
> ct_canvas_save_pgm(&canvas, "ct_plane.pgm");


Elijah
------
the fractal output may even be suited for 2-color pbm output

Chris M. Thomasson

unread,
Jan 25, 2019, 7:33:58 PM1/25/19
to
On 1/25/2019 3:50 PM, Eli the Bearded wrote:
> In comp.lang.c, Chris M. Thomasson <ahh_...@crap.nothing> wrote:
>> Just wondering, when you get some free time, if you can perhaps run the
>> my experimental plotter code that generates a PPM called "ct_plane.ppm".
>> And if you can see a fractal in the resulting rendering. Here is my C99
>> code:
>>
>> https://pastebin.com/raw/322XAnsT
>> (raw text, pure C99: no ads!)
>>
>> It should just compile with GCC, and run like a charm. It has proper
>> aspect ratios so one can use any dimensions they want.
>>
>> Can you notice anything wrong with the code below:
> ...
>> if (fout)
>> {
>> char const ppm_head[] =
>> "P3\n"
>> "# Chris M. Thomasson Simple 2d Plane ver:0.0.0.0 (pre-alpha)";
[...]

> the fractal output may even be suited for 2-color pbm output
>

You are correct. However, I have further plans for this simple code that
can support at least RGB. A simple struct comprising each element in the
buffer for the ct_canvas:

struct ct_rgb
{
unsigned char r;
unsigned char g;
unsigned char b;
};

or even:

struct ct_rgba
{
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};

That would be a decent start.

Bart

unread,
Jan 25, 2019, 7:45:15 PM1/25/19
to
Yes, I had some problems reading the file. My image viewer had to be
extended to support P3 format.

But also, while P3 is a text format, all the data is on one long 14
million character line, so that I wasn't able to use line-oriented
reads, and makes manipulating it in some text editors difficult.

A P5 format (8-bit/binary) would have taken only 2MB instead of 14MB.

Chris M. Thomasson

unread,
Jan 25, 2019, 7:56:01 PM1/25/19
to
I am viewing it in the GIMP 2.8.16. Loads right up. What are you using?
Can you view the PPM at all?

Bart

unread,
Jan 25, 2019, 7:57:50 PM1/25/19
to
On 25/01/2019 22:24, Chris M. Thomasson wrote:
> Just wondering, when you get some free time, if you can perhaps run the
> my experimental plotter code that generates a PPM called "ct_plane.ppm".
> And if you can see a fractal in the resulting rendering. Here is my C99
> code:
>
> https://pastebin.com/raw/322XAnsT
> (raw text, pure C99: no ads!)
>
> It should just compile with GCC, and run like a charm. It has proper
> aspect ratios so one can use any dimensions they want.
>
> Can you notice anything wrong with the code below:

>     FILE* fout = fopen(fname, "w");
>
>     if (fout)
>     {
>         char const ppm_head[] =
>             "P3\n"
>             "# Chris M. Thomasson Simple 2d Plane ver:0.0.0.0
> (pre-alpha)";
>
>         fprintf(fout, "%s\n%lu %lu\n%u\n",
>                 ppm_head,
>                 self->width, self->height,
>                 255U);
>
>         size_t size = self->width * self->height;
>
>         for (size_t i = 0; i < size; ++i)
>         {
>             unsigned int c = self->buf[i];
>             fprintf(fout, "%u %u %u  ", c, 0U, 0U);

Further to the posts about the format, I changed the above lines to:

FILE* fout = fopen(fname, "wb");

if (fout)
{
char const ppm_head[] =
"P6\n"
"# Chris M. Thomasson Simple 2d Plane ver:0.0.0.0 (pre-alpha)";

fprintf(fout, "%s\n%lu %lu\n%u\n",
ppm_head,
self->width, self->height,
255U);

size_t size = self->width * self->height;

for (size_t i = 0; i < size; ++i)
{
unsigned int c = self->buf[i];
fprintf(fout, "%c%c%c", 255-c,255-c,255-c);
}

This give a P6 24/binary format (as you said you were going to use RGB).
I've also written all planes not just R, and took the liberty of
reversing the bytes to get black on white (my preference).

Note the "wb" for the file mode. (These P4/P5/P6 formats are peculiar in
that they mix text and binary data. With "wb", new lines are /not/
written as cr/lf under Windows, but as lf only. I don't know how much
that's likely to screw up some ppm viewers.)

I wasn't sure if %c to write binary bytes would work, but it seems to.

Loading of this format is considerably faster with my viewer.

(To generate binary greyscale, use P5 and write only one %c byte per
pixel. The file extension should also be pgm, although my viewer is not
fussy as it will check the "P" code.)

Bart

unread,
Jan 25, 2019, 7:59:19 PM1/25/19
to
On 26/01/2019 00:55, Chris M. Thomasson wrote:
> On 1/25/2019 4:45 PM, Bart wrote:

>> Yes, I had some problems reading the file. My image viewer had to be
>> extended to support P3 format.
>
> I am viewing it in the GIMP 2.8.16. Loads right up. What are you using?
> Can you view the PPM at all?

Yes I can view it now after I added P3 support. But see my other post
where I tried P6.


Bart

unread,
Jan 26, 2019, 7:35:35 AM1/26/19
to
On 25/01/2019 22:24, Chris M. Thomasson wrote:
> Just wondering, when you get some free time, if you can perhaps run the
> my experimental plotter code that generates a PPM called "ct_plane.ppm".
> And if you can see a fractal in the resulting rendering. Here is my C99
> code:
>
> https://pastebin.com/raw/322XAnsT
> (raw text, pure C99: no ads!)
>
> It should just compile with GCC, and run like a charm. It has proper
> aspect ratios so one can use any dimensions they want.
>
> Can you notice anything wrong with the code below:

Nothing really wrong, but it gives problems on several other compilers:

bcc: no complex.h, no tgmath.h, and lack of complex support (including
in the C library msvcrt.dll even if I added it in the compiler)

tcc: no tgmath.h or complex.h

dmc: no tgmath.h (and doesn't support dynamic struct init)

msvc: no tgmath.h (and loads of errors when you just comment it out.
What is tgmath.h for anyway?)

clang: error: passing '_Complex float' to parameter of incompatible
type '_Fcomplex' (aka 'struct _C_float_complex')
__tg_acos(float _Complex __x) {return cacosf(__x);} etc
^~~
lccwin: compiler failures


I modified it to so that it doesn't use complex.h nor tgmath.h:

https://pastebin.com/raw/tJFxYsb0

Now it runs on all those compilers, except lccwin which appears to have
bugs (it fails on -64, builds on -32, but that doesn't terminate).

Interestingly, all programs seem to have the same runtime: optimisation
makes little difference (all take around 6-7 seconds on my machine, of
which generating the .ppm file is a tiny part).


--
bart

Bart

unread,
Jan 26, 2019, 1:53:37 PM1/26/19
to
On 25/01/2019 22:24, Chris M. Thomasson wrote:
> Just wondering, when you get some free time, if you can perhaps run the
> my experimental plotter code that generates a PPM called "ct_plane.ppm".
> And if you can see a fractal in the resulting rendering. Here is my C99
> code:
>
> https://pastebin.com/raw/322XAnsT
> (raw text, pure C99: no ads!)
>
> It should just compile with GCC, and run like a charm. It has proper
> aspect ratios so one can use any dimensions they want.
>
> Can you notice anything wrong with the code below:

I haven't said anything about the output yet. I get something that
certainly looks like a fractal outline, but there are lots of gaps, and
it's also very jagged.

I don't know enough about it to try anything different. But with the
circles you also draw, they all seem to be drawn with 2048 points
regardless of size, so that some have gaps in their outlines, some have
clusters of pixels.

I tried a variable number of dots inside ct_circle:

n=6.28*radius/plot->plane.xstep; // assumes square aspect

and the results were better. But drawing circles properly is a bit
harder than that, and is best left to a library. Anti-aliasing the
result is another matter too.

Jorgen Grahn

unread,
Jan 26, 2019, 3:29:22 PM1/26/19
to
On Sat, 2019-01-26, Bart wrote:
> On 25/01/2019 23:50, Eli the Bearded wrote:
>> In comp.lang.c, Chris M. Thomasson <ahh_...@crap.nothing> wrote:
>>> Just wondering, when you get some free time, if you can perhaps run the
>>> my experimental plotter code that generates a PPM called "ct_plane.ppm".
>>> And if you can see a fractal in the resulting rendering. Here is my C99
>>> code:
...

>> If you are going to have monochrome output (all red channel here), why
>> not just use the PGM format ("P2")? And since you are using just 0..255,
>> you can very easily use a raw format ("P5" for pgm / "P6" for ppm) and
>> create a much smaller file.
>
> Yes, I had some problems reading the file. My image viewer had to be
> extended to support P3 format.
>
> But also, while P3 is a text format, all the data is on one long 14
> million character line, so that I wasn't able to use line-oriented
> reads, and makes manipulating it in some text editors difficult.
>
> A P5 format (8-bit/binary) would have taken only 2MB instead of 14MB.

Please remember that all those formats are intended as only
intermediates, to be used in Unix pipelines and so on. In situations
where disk space matters, they should have been converted to PNG or
similar.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Bart

unread,
Jan 26, 2019, 4:37:52 PM1/26/19
to
Disk space isn't so important. But it can make it slower.

For example, my ppm readers are written in interpreted code. For
1920x1080 RGB, a P6 binary format be read a row at a time, 5760 bytes.

A P3 text format requires reading 5760 separate values per line. And
because it might not be line-oriented, I had to resort to reading a
character at a time and forming each number.

Reading this size of file in binary is instant; text took 4-5 seconds.

Chris M. Thomasson

unread,
Jan 26, 2019, 9:21:20 PM1/26/19
to
On 1/26/2019 10:53 AM, Bart wrote:
> On 25/01/2019 22:24, Chris M. Thomasson wrote:
>> Just wondering, when you get some free time, if you can perhaps run
>> the my experimental plotter code that generates a PPM called
>> "ct_plane.ppm". And if you can see a fractal in the resulting
>> rendering. Here is my C99 code:
>>
>> https://pastebin.com/raw/322XAnsT
>> (raw text, pure C99: no ads!)
>>
>> It should just compile with GCC, and run like a charm. It has proper
>> aspect ratios so one can use any dimensions they want.
>>
>> Can you notice anything wrong with the code below:
>
> I haven't said anything about the output yet. I get something that
> certainly looks like a fractal outline, but there are lots of gaps, and
> it's also very jagged.

It should look just like:

https://plus.google.com/101799841244447089430/posts/PQ6jekiPpqo

Actually, there are many different fractals one can reap from this, damn
near infinite. Try altering the jp array in ct_ifs to:
___________________
// Compute the fractal
void
ct_ifs(
struct ct_plot* const plot,
unsigned long n
){
// 2 sets
double complex jp[] = {
.0 + 0 * I,
-5.5 + .0 * I
};
___________________

This creates a fractal with no gaps, and a perfect unit circle in the
middle, with spiking antennae. Also, try out:

// 2 sets
double complex jp[] = {
0 + 1 * I,
0 + -1 * I
};

Using better coloring, going to be in the next code, can show the
following fractal from the simple settings above:

https://plus.google.com/101799841244447089430/posts/DZkX1BviSax

The overall idea of ct_ifs can be used to create things like:

https://youtu.be/8mnjzIiizRU

https://youtu.be/1jjiCMC2izc


There are _many_ other fractals one can create just by tweaking the
probability factors used for the random numbers. Some fractals need
special ratios to go into their internal spirals. Others seem to focus
on the border features.




> I don't know enough about it to try anything different. But with the
> circles you also draw, they all seem to be drawn with 2048 points
> regardless of size, so that some have gaps in their outlines, some have
> clusters of pixels.
>
> I tried a variable number of dots inside ct_circle:
>
>   n=6.28*radius/plot->plane.xstep;       // assumes square aspect
>
> and the results were better. But drawing circles properly is a bit
> harder than that, and is best left to a library. Anti-aliasing the
> result is another matter too.
>

Big time. Things will get better in further versions. I am trying to
isolate the basics in this c99 code.

Thank you so much. Just reading this now, and will compile your
alterations to my code.

Jorgen Grahn

unread,
Jan 27, 2019, 7:11:45 AM1/27/19
to
A third option is to read a few kilobytes at a time, form the numbers,
and keep a remainder buffer for the case where you cut a number in
half with your read.

(Or you can use libpnm, but I suspect you want to do it yourself.)

> Reading this size of file in binary is instant; text took 4-5 seconds.

David Brown

unread,
Jan 27, 2019, 7:20:56 AM1/27/19
to
On 26/01/2019 13:35, Bart wrote:
> On 25/01/2019 22:24, Chris M. Thomasson wrote:
>> Just wondering, when you get some free time, if you can perhaps run
>> the my experimental plotter code that generates a PPM called
>> "ct_plane.ppm". And if you can see a fractal in the resulting
>> rendering. Here is my C99 code:
>>
>> https://pastebin.com/raw/322XAnsT
>> (raw text, pure C99: no ads!)
>>
>> It should just compile with GCC, and run like a charm. It has proper
>> aspect ratios so one can use any dimensions they want.
>>
>> Can you notice anything wrong with the code below:
>
> Nothing really wrong, but it gives problems on several other compilers:
>
> bcc: no complex.h, no tgmath.h, and lack of complex support (including
> in the C library msvcrt.dll even if I added it in the compiler)
>
> tcc: no tgmath.h or complex.h
>
> dmc: no tgmath.h (and doesn't support dynamic struct init)
>
> msvc: no tgmath.h (and loads of errors when you just comment it out.
> What is tgmath.h for anyway?)
>


Chris said it was a C99 program. A C implementation that does not have
tgmath.h, complex.h, and the required libraries is not a C99
implementation. So it is entirely reasonable that his program does not
work on systems that don't support C99 - the standard is only 20 years
old, and not every tool has caught up yet.

> clang: error: passing '_Complex float' to parameter of incompatible
>  type '_Fcomplex' (aka 'struct _C_float_complex')
>     __tg_acos(float _Complex __x) {return cacosf(__x);} etc
>                                                  ^~~

I wonder if that is something specific to clang on Windows using MS's
headers? (This is purely speculation, not based on any experience of
the tool on Windows.)

> lccwin: compiler failures
>
>
> I modified it to so that it doesn't use complex.h nor tgmath.h:
>
>   https://pastebin.com/raw/tJFxYsb0
>

Neither complex numbers nor the generic maths functions are that hard to
duplicate in old C90. They are just more convenient in C99.

> Now it runs on all those compilers, except lccwin which appears to have
> bugs (it fails on -64, builds on -32, but that doesn't terminate).
>
> Interestingly, all programs seem to have the same runtime: optimisation
> makes little difference (all take around 6-7 seconds on my machine, of
> which generating the .ppm file is a tiny part).
>

Depending on the cpu you are running on, that is not too surprising for
a program that works with a lot of floating point calculations,
especially things like square roots and division. These usually take a
fair amount of clock cycles - and the cpu will do plenty of other
"simple" things while waiting. Thus optimisation of other parts -
function calls, loops, data movement, etc., will have little effect
here. Modern x86 cpus are very good at running sub-optimal code. It is
an interesting point to consider.

Still, in my testing with gcc 5.4 (64-bit Linux), unoptimised code took
0.68s, while with reasonable fast options "-O3 -ffast-math
-march=native" it takes 0.44 seconds.

Since that is an order of magnitude faster than your system, and my PC
is not usually /that/ much faster than yours, it could well be the
effect of scheduling, pipelining, and out-of-order execution on the
particular cpu.

Ben Bacarisse

unread,
Jan 27, 2019, 8:40:41 AM1/27/19
to
Jorgen Grahn <grahn...@snipabacken.se> writes:

> On Sat, 2019-01-26, Bart wrote:
<snip>
>> A P3 text format requires reading 5760 separate values per line. And
>> because it might not be line-oriented, I had to resort to reading a
>> character at a time and forming each number.
>
> A third option is to read a few kilobytes at a time, form the numbers,
> and keep a remainder buffer for the case where you cut a number in
> half with your read.

Or you can use fscanf if you just want to get a solution quickly.

--
Ben.

Bart

unread,
Jan 27, 2019, 9:11:07 AM1/27/19
to
On 27/01/2019 12:20, David Brown wrote:
> On 26/01/2019 13:35, Bart wrote:

>> msvc: no tgmath.h (and loads of errors when you just comment it out.
>> What is tgmath.h for anyway?)
>>
>
>
> Chris said it was a C99 program.

Most of those compilers, except for bcc, are reasonably full c99
compilers. Yet tgmath.h, which varies from 130 to 1400 lines where it
exists, seems to be missing from dmc, tcc and msvc.

  A C implementation that does not have
> tgmath.h, complex.h, and the required libraries is not a C99
> implementation.  So it is entirely reasonable that his program does not
> work on systems that don't support C99 - the standard is only 20 years
> old, and not every tool has caught up yet.

No, it's just support for tgmath.h that is not so universal. Actually
I'm still not sure what it does.

>> clang: error: passing '_Complex float' to parameter of incompatible
>>   type '_Fcomplex' (aka 'struct _C_float_complex')
>>      __tg_acos(float _Complex __x) {return cacosf(__x);} etc
>>                                                   ^~~
>
> I wonder if that is something specific to clang on Windows using MS's
> headers?  (This is purely speculation, not based on any experience of
> the tool on Windows.)

This is an odd one: clang seems to rely on msvc somehow, but msvc
doesn't have tgmath,h, yet clang has.

>> Interestingly, all programs seem to have the same runtime:
>> optimisation makes little difference (all take around 6-7 seconds on
>> my machine, of which generating the .ppm file is a tiny part).
>>
>
> Depending on the cpu you are running on, that is not too surprising for
> a program that works with a lot of floating point calculations,
> especially things like square roots and division.
...
> Still, in my testing with gcc 5.4 (64-bit Linux), unoptimised code took
> 0.68s, while with reasonable fast options "-O3 -ffast-math
> -march=native" it takes 0.44 seconds.

> Since that is an order of magnitude faster than your system, and my PC
> is not usually /that/ much faster than yours, it could well be the
> effect of scheduling, pipelining, and out-of-order execution on the
> particular cpu.

Actually there is something I overlooked completely, which is the printf
that is called on every point. It overwrites the same line so you don't
appreciate how many lines are displayed.

Without that, then it was much faster (certainly on Windows). Fast
enough that generating the .ppm was was significant.

So without the ppm, and without printf, timings varied from 0.75 seconds
to 1.2 seconds, with gcc-O3 fastest.

Using your additional options, gcc took 0.5 seconds. Just over twice as
fast as my unoptimised bcc (1.1) or gcc-O0 (1.15); this is where the
floating point overheads limit the difference.


David Brown

unread,
Jan 27, 2019, 10:36:48 AM1/27/19
to
On 27/01/2019 15:13, Bart wrote:
> On 27/01/2019 12:20, David Brown wrote:
>> On 26/01/2019 13:35, Bart wrote:
>
>>> msvc: no tgmath.h (and loads of errors when you just comment it out.
>>> What is tgmath.h for anyway?)
>>>
>>
>>
>> Chris said it was a C99 program.
>
> Most of those compilers, except for bcc, are reasonably full c99
> compilers. Yet tgmath.h, which varies from 130 to 1400 lines where it
> exists, seems to be missing from dmc, tcc and msvc.
>

As you know, a "C implementation" consists of a C compiler, headers, a
standard library, and whatever other bits and pieces are needed to turn
C code into an executable (assembler, linker, etc.). A compiler that
can handle C99 syntax is only part of the system - if you don't have the
C99 headers, you don't have a C99 implementation. That seems to be the
case for these C implementations - at least with the installation on
your system (maybe other tcc or dmc installations have C99 headers and
libraries).

>   A C implementation that does not have
>> tgmath.h, complex.h, and the required libraries is not a C99
>> implementation.  So it is entirely reasonable that his program does
>> not work on systems that don't support C99 - the standard is only 20
>> years old, and not every tool has caught up yet.
>
> No, it's just support for tgmath.h that is not so universal. Actually
> I'm still not sure what it does.
>

At the risk of stating the blindingly obvious, "tgmath.h" is a header of
type-generic maths macros. It is documented in the C99 (and C11, and
C17) standards. And if you don't like reading the standards documents
(which is not unreasonable - they are not always easy to read), this
site is a good reference for C and C++:

<https://en.cppreference.com/w/c/numeric/tgmath>

I can't tell you how universal support for tgmath.h is. For my modest C
needs on big systems, my gcc installations support it fine. For my
embedded programming, I usually write my own maths functions anyway and
thus have little need of <tgmath.h> or even <math.h>


>>> clang: error: passing '_Complex float' to parameter of incompatible
>>>   type '_Fcomplex' (aka 'struct _C_float_complex')
>>>      __tg_acos(float _Complex __x) {return cacosf(__x);} etc
>>>                                                   ^~~
>>
>> I wonder if that is something specific to clang on Windows using MS's
>> headers?  (This is purely speculation, not based on any experience of
>> the tool on Windows.)
>
> This is an odd one: clang seems to rely on msvc somehow, but msvc
> doesn't have tgmath,h, yet clang has.
>

MSVC has famously bad C99 support. clang has perfectly good C99
support. But this sort of thing lies partly between the compiler and
the library (the two main parts of the C implementation). <tgmath.h>
cannot be written in standard C (until C11) - it requires
compiler-specific features. So it is one of the headers that is likely
to come with the compiler rather than the library. On the other hand,
the /implementations/ of the functions used will be part of the C
run-time library. MSVC uses highly inefficient DLL's for this, and
their DLL's don't have good C99 support. So it does not surprise me
that the hack they have made with fitting clang onto MSVC results in the
<tgmath.h> header being fine while missing the functions in the library.
(The same applies if you are using MingW rather than MSVC - that's why
I recommended MingW-64.) I'd imagine that with enough effort, you could
find options or libraries to handle this, or you could install clang
along with fuller libraries.

>>> Interestingly, all programs seem to have the same runtime:
>>> optimisation makes little difference (all take around 6-7 seconds on
>>> my machine, of which generating the .ppm file is a tiny part).
>>>
>>
>> Depending on the cpu you are running on, that is not too surprising
>> for a program that works with a lot of floating point calculations,
>> especially things like square roots and division.
> ...
>> Still, in my testing with gcc 5.4 (64-bit Linux), unoptimised code
>> took 0.68s, while with reasonable fast options "-O3 -ffast-math
>> -march=native" it takes 0.44 seconds.
>
>> Since that is an order of magnitude faster than your system, and my PC
>> is not usually /that/ much faster than yours, it could well be the
>> effect of scheduling, pipelining, and out-of-order execution on the
>> particular cpu.
>
> Actually there is something I overlooked completely, which is the printf
> that is called on every point. It overwrites the same line so you don't
> appreciate how many lines are displayed.
>

You are right - I never noticed that (I didn't look much at the program
itself). Yes, this will dominate.

> Without that, then it was much faster (certainly on Windows). Fast
> enough that generating the .ppm was was significant.
>
> So without the ppm, and without printf, timings varied from 0.75 seconds
> to 1.2 seconds, with gcc-O3 fastest.
>
> Using your additional options, gcc took 0.5 seconds. Just over twice as
> fast as my unoptimised bcc (1.1) or gcc-O0 (1.15); this is where the
> floating point overheads limit the difference.
>

Interesting.



Keith Thompson

unread,
Jan 27, 2019, 4:09:22 PM1/27/19
to
Bart <b...@freeuk.com> writes:
> On 27/01/2019 12:20, David Brown wrote:
>> On 26/01/2019 13:35, Bart wrote:
>>> msvc: no tgmath.h (and loads of errors when you just comment it out.
>>> What is tgmath.h for anyway?)
>>
>> Chris said it was a C99 program.
>
> Most of those compilers, except for bcc, are reasonably full c99
> compilers. Yet tgmath.h, which varies from 130 to 1400 lines where it
> exists, seems to be missing from dmc, tcc and msvc.
>
>   A C implementation that does not have
>> tgmath.h, complex.h, and the required libraries is not a C99
>> implementation.  So it is entirely reasonable that his program does not
>> work on systems that don't support C99 - the standard is only 20 years
>> old, and not every tool has caught up yet.
>
> No, it's just support for tgmath.h that is not so universal. Actually
> I'm still not sure what it does.

tgmath.h is a header that is required by the C99 standard. It is
not optional. An implementation that doesn't provide tgmath.h is
not a complete C99 implementation.

tgmath.h is part of the runtime library, not of the compiler.
Since you're on Windows, I suspect that the implementations that
don't provide tgmath.h are all using the same Microsoft headers, and
that those headers don't include tgmath.h. Other implementations
might use the Microsoft headers but provide some of their own
headers to supplement them, or use a different set of headers.

As for what tgmath.h is for, there are numerous resources,
including the C standard itself, that you can use to find out.
If you've consulted those resources and still have questions,
by all means ask them.

[...]

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

Chris M. Thomasson

unread,
Jan 27, 2019, 5:28:44 PM1/27/19
to
Well, "almost" every point in the grand scheme of things. In the ct_ifs
function the output is within the damn fractal computation loop.

if (! (i % 256))
{
printf("rendering: %lu of %lu\r", i + 1, n);
}

That mod is not pretty as well...

_________________
//****** Output in 256 chunks.
if (! (i % 256))
{
printf("rendering: %lu of %lu\r", i + 1, n);
}
}

printf("rendering: %lu of %lu\n", n, n);
}
_________________

Removing this "pointless, in a sense... progress" output will greatly
increase the speed of the fractal calculations. I just left it in there
so I could see the damn progress. I should use a fflush(stdout) after
each call to printf to make it even more inefficient. ;^)


Sorry about that.

Chris M. Thomasson

unread,
Jan 27, 2019, 6:37:57 PM1/27/19
to
[...]

Your conversion to P6 24/binary works great. The black on white is nice
as well. I really need to put in some log-density coloring.

Thanks Bart.

Bart

unread,
Jan 27, 2019, 6:53:30 PM1/27/19
to
On 27/01/2019 22:28, Chris M. Thomasson wrote:
> On 1/27/2019 7:36 AM, David Brown wrote:
>> On 27/01/2019 15:13, Bart wrote:

>>> Actually there is something I overlooked completely, which is the
>>> printf that is called on every point.

>         if (! (i % 256))
>         {
>             printf("rendering: %lu of %lu\r", i + 1, n);
>         }
>     }

> }
> _________________
>
> Removing this "pointless, in a sense... progress" output will greatly
> increase the speed of the fractal calculations. I just left it in there
> so I could see the damn progress. I should use a fflush(stdout) after
> each call to printf to make it even more inefficient. ;^)

Yeah, it's not really every point, but every 256th. Although that still
makes it dominate the run-time.

I tried a printf literally on every point, and it took 12 minutes,
compared with 1-2 seconds without any printing.

Chris M. Thomasson

unread,
Jan 29, 2019, 1:20:47 AM1/29/19
to
On 1/25/2019 2:24 PM, Chris M. Thomasson wrote:
> Just wondering, when you get some free time, if you can perhaps run the
> my experimental plotter code that generates a PPM called "ct_plane.ppm".
> And if you can see a fractal in the resulting rendering. Here is my C99
> code:
>
> https://pastebin.com/raw/322XAnsT
> (raw text, pure C99: no ads!)
>
> It should just compile with GCC, and run like a charm. It has proper
> aspect ratios so one can use any dimensions they want.
[...]
Fwiw, just a little addition that allows for the space of rgb
per-element color of the canvas buffer. However, the fractal now
dynamically adds colors to the red channel only. The output PPM looks
dark, but has the beginnings of the log-density plot I am looking for.
The log-density can allow one to brighten up the dark spots. Adding more
iterations to CT_N helps, but increases run time. Exposing this code in
stages. Btw, it now uses P6, using Bart's kind addition from:

https://groups.google.com/forum/#!original/comp.lang.c/4196m3Raggs/2moZ67o5EwAJ

For Bart, wrt the output frequency, I kept it in, but altered the
condition to:

if (! (i % (n / 3)))
{
printf("rendering: %lu of %lu\r", i + 1, n);
}

;^)

Anyway, here is the code, this is going to go up on GitHub pretty soon:

https://pastebin.com/raw/SyDWnfY7

The resulting PPM should look like:

https://plus.google.com/101799841244447089430/posts/Lx9z1ScK3eE

With the next version, it will look more like, I will perhaps add in a
pure escape time fractal, instead of reverse iteration:

https://plus.google.com/101799841244447089430/posts/DZkX1BviSax


Progress in this 100% portable plotter is being made, slowly but surely.
Expect much more colorful results. My current C++ code uses Cairo, and
is too big and cumbersome. I am doing this to get to a 100% portable
standalone C99 version, lean and mean:
_______________
/*
A Simple 2d Plane For Hugh, with proper aspect ratios!
Color adder, single channel
By: Chris M. Thomasson
*_____________________________________________________________*/


#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <complex.h>
#include <tgmath.h>
#include <stdbool.h>


#define CT_WIDTH 1920
#define CT_HEIGHT 1080
#define CT_N 10000000



struct ct_rgb
{
unsigned char r;
unsigned char g;
unsigned char b;
};


struct ct_canvas
{
unsigned long width;
unsigned long height;
//unsigned char* buf;
struct ct_rgb* buf;
};

bool
ct_canvas_create(
struct ct_canvas* const self,
unsigned long width,
unsigned long height
){
size_t size = width * height * sizeof(*self->buf);

self->buf = calloc(1, size);

if (self->buf)
{
self->width = width;
self->height = height;

return true;
}

return false;
}

void
ct_canvas_destroy(
struct ct_canvas const* const self
){
free(self->buf);
}

bool
ct_canvas_save_ppm(
struct ct_canvas const* const self,
char const* fname
){
FILE* fout = fopen(fname, "wb");

if (fout)
{
// P6 by Bart over on:
//
https://groups.google.com/forum/#!original/comp.lang.c/4196m3Raggs/2moZ67o5EwAJ

char const ppm_head[] =
"P6\n"
"# Chris M. Thomasson Simple 2d Plane ver:0.0.0.0 (pre-alpha)";

fprintf(fout, "%s\n%lu %lu\n%u\n",
ppm_head,
self->width, self->height,
255U);

size_t size = self->width * self->height;

for (size_t i = 0; i < size; ++i)
{
//unsigned int c = self->buf[i];
struct ct_rgb* c = self->buf + i;

fprintf(fout, "%c%c%c", c->r, c->g, c->b);
//fprintf(fout, "%c%c%c", c, 0U, 0U);
ct_plot_add(
struct ct_plot* const self,
double complex z,
struct ct_rgb const* color
){
long x = (creal(z) - self->plane.axes.xmin) / self->plane.xstep;
long y = (self->plane.axes.ymax - cimag(z)) / self->plane.ystep;

if (x > -1 && x < (long)self->canvas->width &&
y > -1 && y < (long)self->canvas->height)
{
// Now, we can convert to index.
size_t i = x + y * self->canvas->width;

assert(i < self->canvas->height * self->canvas->width);

struct ct_rgb exist = self->canvas->buf[i];

if (exist.r + 1 != 0)
{
exist.r += 1;
}

self->canvas->buf[i] = exist;
return true;
}

return true;
}


bool
ct_plot_point(
struct ct_plot* const self,
double complex z,
struct ct_rgb const* color
){
long x = (creal(z) - self->plane.axes.xmin) / self->plane.xstep;
long y = (self->plane.axes.ymax - cimag(z)) / self->plane.ystep;

if (x > -1 && x < (long)self->canvas->width &&
y > -1 && y < (long)self->canvas->height)
{
// Now, we can convert to index.
size_t i = x + y * self->canvas->width;

assert(i < self->canvas->height * self->canvas->width);

self->canvas->buf[i] = *color;
return true;
}

return false;
}



// Compute the fractal
void
ct_ifs(
struct ct_plot* const plot,
unsigned long n
){
// 2 sets
/*
double complex jp[] = {
.5 + 0 * I,
-5.5 + 0 * I
};
*/

double complex jp[] = {
0 + 1 * I,
0 + -1 * I
};

double complex z = 0+0*I;
double complex c = 0+0*I;

for (unsigned long i = 0; i < n; ++i)
{
double rn0 = rand() / (RAND_MAX - .0);
double rn1 = rand() / (RAND_MAX - .0);

if (rn0 > .5)
{
c = jp[0];
}

else
{
c = jp[1];
}


double complex d = z - c;
double complex root = csqrt(d);

z = root;

if (rn1 > .5)
{
z = -root;
}

struct ct_rgb rgb = { 255, 0, 0 };

// ct_plot_point(plot, z, &rgb);
// ct_plot_point(plot, root, &rgb);
ct_plot_add(plot, z, &rgb);
ct_plot_add(plot, root, &rgb);

if (! (i % (n / 3)))
{
printf("rendering: %lu of %lu\r", i + 1, n);
}
}

printf("rendering: %lu of %lu\n", n, n);
}





// slow, so what for now... ;^)
void ct_circle(
struct ct_plot* const plot,
double complex c,
double radius,
unsigned int n
){
double abase = 6.2831853071 / n;

for (unsigned int i = 0; i < n; ++i)
{
double angle = abase * i;

double complex z =
(creal(c) + cos(angle) * radius) +
(cimag(c) + sin(angle) * radius) * I;

struct ct_rgb rgb = { 255, 255, 255 };

ct_plot_point(plot, z, &rgb);
}
}


int main(void)
{
struct ct_canvas canvas;

if (ct_canvas_create(&canvas, CT_WIDTH, CT_HEIGHT))
{
double complex plane_origin = 0+0*I;
double plane_radius = 2.0;

struct ct_axes axes = ct_axes_from_point(plane_origin,
plane_radius);

struct ct_plot plot;
ct_plot_init(&plot, &axes, &canvas);


ct_ifs(&plot, CT_N);

// Our unit circle
ct_circle(&plot, 0+0*I, 1.0, 2048);
ct_circle(&plot, 2+0*I, 2.0, 2048);
ct_circle(&plot, -2+0*I, 2.0, 2048);
ct_circle(&plot, 0+2*I, 2.0, 2048);
ct_circle(&plot, 0-2*I, 2.0, 2048);

ct_canvas_save_ppm(&canvas, "ct_plane.ppm");

ct_canvas_destroy(&canvas);

return EXIT_SUCCESS;
}

return EXIT_FAILURE;
}
_______________


Does it work for you?


Chris M. Thomasson

unread,
Jan 29, 2019, 1:55:16 AM1/29/19
to
[...]

The next version of the code that will be up on github is going to show
how to get the following fractal coloring:

https://plus.google.com/101799841244447089430/posts/Lx9z1ScK3eE

Very simple at first, not even using any log-density formulas.

Chris M. Thomasson

unread,
Jan 29, 2019, 2:03:46 AM1/29/19
to
On 1/28/2019 10:55 PM, Chris M. Thomasson wrote:
[...]
> The next version of the code that will be up on github is going to show
> how to get the following fractal coloring:
>
> https://plus.google.com/101799841244447089430/posts/Lx9z1ScK3eE

wrong link to the image damn it!

https://plus.google.com/101799841244447089430/posts/f7w8V3VGHgY

Sorry about that. Notice how the rendering is a lot brighter, and has
more detail?

Chris M. Thomasson

unread,
Jan 29, 2019, 4:11:34 PM1/29/19
to
On 1/27/2019 7:36 AM, David Brown wrote:
> On 27/01/2019 15:13, Bart wrote:
>> On 27/01/2019 12:20, David Brown wrote:
>>> On 26/01/2019 13:35, Bart wrote:
>>
>>>> msvc: no tgmath.h (and loads of errors when you just comment it out.
>>>> What is tgmath.h for anyway?)
>>>>
>>>
>>>
>>> Chris said it was a C99 program.

[...]

I thank Bart for removing the dependency of tgmath.h and complex.h.
However, C99 is just to convenient. I am sticking with it for several
reasons, one of which is I like the way it can handle complex numbers so
naturally. 1+0*I is just so easy, right in the language itself.

Bart

unread,
Jan 29, 2019, 5:23:33 PM1/29/19
to
I wanted to try it on mine, and was actually about to add some minimal
complex support, then I found few other compilers had much luck with it.
(Only gcc and Pelles C managed to compile and run it.)

I have found that tgmath.h is not necessary, math.h will do (for gcc and
pelles c), but that still doesn't help make it work on other compilers,
so it might as well be left.

(I won't now attempt complex support on my bcc, however I've actually
just started designing a new language, and have added the complex type
to the list of built-in types. So I'll direct my efforts at that new
project.)

Chris M. Thomasson

unread,
Feb 4, 2019, 6:31:05 PM2/4/19
to
Great. GCC works like a charm for this. Also, it is 100% standard C99,
so... If one claims C99, then it should handle my program like a
champion. Right?

Bart

unread,
Feb 4, 2019, 8:26:12 PM2/4/19
to
In theory.

If you want to stipulate only gcc (and perhaps only Linux) for building
your program, then that's fine if it's the simplest solution for you.

Ian Collins

unread,
Feb 4, 2019, 10:14:20 PM2/4/19
to
Chris said it was a C99 program.

--
Ian.

David Brown

unread,
Feb 5, 2019, 5:38:16 AM2/5/19
to
It sounded like he was stipulating C99, not gcc. C99 is twenty years
old - it is absurd for anyone to make or promote general purpose C
compilers that are not C99 compatible. Any tool that wants to be taken
seriously as a C compiler will handle it.

(If you want to make a compiler that handles a subset of C as generated
by a specific translation program for another language, then that is
fine - but then it is a compiler for a niche purpose, not a general C
compiler.)

Bart

unread,
Feb 5, 2019, 6:14:52 AM2/5/19
to
On 05/02/2019 10:38, David Brown wrote:
> On 05/02/2019 02:26, Bart wrote:
>> On 04/02/2019 23:30, Chris M. Thomasson wrote:
>>> On 1/29/2019 2:23 PM, Bart wrote:
>>
>>>> I wanted to try it on mine, and was actually about to add some
>>>> minimal complex support, then I found few other compilers had much
>>>> luck with it. (Only gcc and Pelles C managed to compile and run it.)
>>>>
>>>> I have found that tgmath.h is not necessary, math.h will do (for gcc
>>>> and pelles c), but that still doesn't help make it work on other
>>>> compilers, so it might as well be left.
>>>>
>>>> (I won't now attempt complex support on my bcc, however I've actually
>>>> just started designing a new language, and have added the complex
>>>> type to the list of built-in types. So I'll direct my efforts at that
>>>> new project.)
>>>
>>> Great. GCC works like a charm for this. Also, it is 100% standard C99,
>>> so... If one claims C99, then it should handle my program like a
>>> champion. Right?
>>
>> In theory.
>>
>> If you want to stipulate only gcc (and perhaps only Linux) for building
>> your program, then that's fine if it's the simplest solution for you.
>>
>
> It sounded like he was stipulating C99, not gcc. C99 is twenty years
> old - it is absurd for anyone to make or promote general purpose C
> compilers that are not C99 compatible. Any tool that wants to be taken
> seriously as a C compiler will handle it.

And as I said, only 2 of my 8 C Windows compilers managed to compile
that code. 7 of them purport to be C99 compilers (7 support designated
initialisers, 6 VLAs, 6 (a different 6) compound literals).

clang will compile it on rextester.com, but not on my Windows machine.

That's why I suggested gcc+Linux (a combination that can be tested by
the author). If it works with anything else, that's a bonus.

> (If you want to make a compiler that handles a subset of C as generated
> by a specific translation program for another language, then that is
> fine - but then it is a compiler for a niche purpose, not a general C
> compiler.)

Chris does like to push the boundaries by making full use of the newest
and most advanced features (he's even eagerly awaiting CAlive). Most
code I've tried to compile is more conservative (or maybe just older,
but not 20 years older). VLAs, designated initialisers and compound
literals are unusual.

I wouldn't call anything that doesn't support those a niche compiler, as
it can still cope with a vast number of useful programs.


David Brown

unread,
Feb 5, 2019, 8:35:52 AM2/5/19
to
So 6 of them are not C99 compatible, and are not serious general purpose
C compiler implementations - assuming, of course, that you know how to
use them properly and choose appropriate compilation modes.

> clang will compile it on rextester.com, but not on my Windows machine.

So the clang you have on Windows is not a serious C implementation
(again assuming you know how to use it). Since clang supports C99
language perfectly well, I expect the problem is with the C library used
in the package you have for clang.

>
> That's why I suggested gcc+Linux (a combination that can be tested by
> the author). If it works with anything else, that's a bonus.
>

gcc worked fine on my Windows machine. clang worked fine on Linux (I
don't have it on my Windows system to test it).

It is C99 code, and C99 implementations will handle it fine.

>> (If you want to make a compiler that handles a subset of C as generated
>> by a specific translation program for another language, then that is
>> fine - but then it is a compiler for a niche purpose, not a general C
>> compiler.)
>
> Chris does like to push the boundaries by making full use of the newest
> and most advanced features (he's even eagerly awaiting CAlive). Most
> code I've tried to compile is more conservative (or maybe just older,
> but not 20 years older). VLAs, designated initialisers and compound
> literals are unusual.

You think these are "new and advanced" - surely you jest. These are all
common constructs - people use them whenever they need them, and when
they make code simpler, clearer or safer (as they can do all of that).
People were using them long before C99 came out, as compiler extensions
- they were added to C99 because people /wanted/ them.

>
> I wouldn't call anything that doesn't support those a niche compiler, as
> it can still cope with a vast number of useful programs.
>

OK. Call it "outdated compiler", or "toy compiler" if you prefer.
Don't call it a "C compiler", because that implies real, modern C. (It
actually implies C17 support, since that is the current version of C.)


Bart

unread,
Feb 5, 2019, 10:06:59 AM2/5/19
to
On 05/02/2019 13:35, David Brown wrote:
> On 05/02/2019 12:14, Bart wrote:

>> And as I said, only 2 of my 8 C Windows compilers managed to compile
>> that code. 7 of them purport to be C99 compilers (7 support designated
>> initialisers, 6 VLAs, 6 (a different 6) compound literals).
>>
>
> So 6 of them are not C99 compatible, and are not serious general purpose
> C compiler implementations

MSVC? Surely /you/ jest?


> It is C99 code, and C99 implementations will handle it fine.

Which are? Have a look here:

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

(You may need to expand the Implementations section.)

There is a long list of compilers with partial C99 support. According to
you, such compilers are worthless toys.

Note that this article calls them all "C compilers". Again, according to
you, they mustn't call them C compilers unless they have full C99
compliance. How about those that have full C99, but partial C11 support?

Interestingly, both clang and gcc are listed as having 'Mostly' C99
support (I don't know how old the article is). So before 2014 anyway,
not even gcc was a proper C compiler according to your own standards!
2014 was 15 years after 1999.

> You think these are "new and advanced" - surely you jest.

He makes uses of _Generic (C11) and things like _Atomic (C11) and
threads (C11) (not in this code but elsewhere).

>> I wouldn't call anything that doesn't support those a niche compiler, as
>> it can still cope with a vast number of useful programs.
>>
>
> OK. Call it "outdated compiler", or "toy compiler" if you prefer.
> Don't call it a "C compiler", because that implies real, modern C. (It
> actually implies C17 support, since that is the current version of C.)

So as soon as the first C17 compiler comes out, all other C compilers
become toys?

Nonsense.

David Brown

unread,
Feb 5, 2019, 11:02:28 AM2/5/19
to
On 05/02/2019 16:06, Bart wrote:
> On 05/02/2019 13:35, David Brown wrote:
>> On 05/02/2019 12:14, Bart wrote:
>
>>> And as I said, only 2 of my 8 C Windows compilers managed to compile
>>> that code. 7 of them purport to be C99 compilers (7 support designated
>>> initialisers, 6 VLAs, 6 (a different 6) compound literals).
>>>
>>
>> So 6 of them are not C99 compatible, and are not serious general purpose
>> C compiler implementations
>
> MSVC? Surely /you/ jest?
>

No, MSVC has been a laughing stock amongst C programmers for a very long
time, because it does not support modern C. It is a fine C++ tool, but
a terrible C tool. (Though as I understand it, recent versions have
improved somewhat.) MSVC has single-handedly restricted the use of C on
Windows by failing to support C99.

>
>> It is C99 code, and C99 implementations will handle it fine.
>
> Which are? Have a look here:
>
> https://en.wikipedia.org/wiki/C99
>
> (You may need to expand the Implementations section.)
>
> There is a long list of compilers with partial C99 support. According to
> you, such compilers are worthless toys.
>

Many (such as gcc and clang) only say "mostly" or "partial" because they
don't include all optional features of C99. Pretty much the only
missing points are some floating point pragmas and universal character
identifiers. There is a difference between such minor points, and major
features like mixed declarations and statements.

(Complex numbers are a bit odd, in that they are made optional in C11.)

> Note that this article calls them all "C compilers". Again, according to
> you, they mustn't call them C compilers unless they have full C99
> compliance. How about those that have full C99, but partial C11 support?
>

I can appreciate compilers not having C11 support - it is not a commonly
used standard.

> Interestingly, both clang and gcc are listed as having 'Mostly' C99
> support (I don't know how old the article is). So before 2014 anyway,
> not even gcc was a proper C compiler according to your own standards!
> 2014 was 15 years after 1999.

Again, look at the details. People have been writing C99 code with gcc
for a very long time (though it did take a number of years before gcc's
C99 support was practically complete).


>
>> You think these are "new and advanced" - surely you jest.
>
> He makes uses of _Generic (C11) and things like _Atomic (C11) and
> threads (C11) (not in this code but elsewhere).

And that is fine. These features exist for a reason - if his code is
better with these, then he should use them.

>
>>> I wouldn't call anything that doesn't support those a niche compiler, as
>>> it can still cope with a vast number of useful programs.
>>>
>>
>> OK.  Call it "outdated compiler", or "toy compiler" if you prefer.
>> Don't call it a "C compiler", because that implies real, modern C.  (It
>> actually implies C17 support, since that is the current version of C.)
>
> So as soon as the first C17 compiler comes out, all other C compilers
> become toys?
>

No, I did not say anything like that.

> Nonsense.

Bart

unread,
Feb 5, 2019, 2:07:16 PM2/5/19
to
On 05/02/2019 16:02, David Brown wrote:
> On 05/02/2019 16:06, Bart wrote:


> I can appreciate compilers not having C11 support - it is not a commonly
> used standard.

But if Chris used C11-only features that weren't supported by a
compiler, what you be as dismissive of it as you are with the likes of MSVC?


> Again, look at the details. People have been writing C99 code with gcc
> for a very long time (though it did take a number of years before gcc's
> C99 support was practically complete).


>>> OK.  Call it "outdated compiler", or "toy compiler" if you prefer.
>>> Don't call it a "C compiler", because that implies real, modern C.  (It
>>> actually implies C17 support, since that is the current version of C.)
>>
>> So as soon as the first C17 compiler comes out, all other C compilers

(I've just realised 2017 has come and gone! Are there any actual C17
compilers?)

> No, I did not say anything like that.
>
>> Nonsense.

You said this:

"So 6 of them are not C99 compatible, and are not serious general
purpose compiler implementations"

Why C99? The current C standard is C11, which is now 8 years old. Yet
you seem to consider one that doesn't support 100% C11 as still a
serious contender, but one that doesn't support 100% C99 as not serious.
Or a toy.

That it took so many years to get widespread C99 support suggests to me
that it wasn't easy to do. (I know you will argue every single C99
feature is trivial to support.) And it took gcc apparently over a decade.

So it does seem to me that you are being unduly harsh on any
implementation that hasn't yet, or doesn't want to, support full C99.
It didn't stop gcc from being used seriously during the years it took to
achieve full compliance.

These are some significant C99 features that my bcc project doesn't
support (and won't do as I've lost interest):

- VLAs including variable types as parameters
- Designated initialisers
- Compound literals
- Complex types (might have been pre-C99)

Interestingly, many of those aren't supported by C++ either, so I'm not
going to lose any sleep over them.

Ian Collins

unread,
Feb 5, 2019, 2:13:08 PM2/5/19
to
On 06/02/2019 08:07, Bart wrote:
>
> These are some significant C99 features that my bcc project doesn't
> support (and won't do as I've lost interest):
>
> - VLAs including variable types as parameters
> - Designated initialisers
> - Compound literals
> - Complex types (might have been pre-C99)
>
> Interestingly, many of those aren't supported by C++ either, so I'm not
> going to lose any sleep over them.

I bet the MSVC compiler writers said much the same!

The "improved" C99 support in MSVC appeared because the required C99
subset for C++ has grown in recent standard revisions.

--
Ian.

Keith Thompson

unread,
Feb 5, 2019, 3:49:26 PM2/5/19
to
Bart <b...@freeuk.com> writes:
[...]
> (I've just realised 2017 has come and gone! Are there any actual C17
> compilers?)
[...]

C17 doesn't differ substantially from C11. According to the gcc 8.2.0
documentation:

A fourth version of the C standard, known as "C11", was published
in 2011 as ISO/IEC 9899:2011. (While in development, drafts
of this standard version were referred to as "C1X".) GCC has
substantially complete support for this standard, enabled with
'-std=c11' or '-std=iso9899:2011'. A version with corrections
integrated is known as "C17" and is supported with '-std=c17'
or '-std=iso9899:2017'; the corrections are also applied with
'-std=c11', and the only difference between the options is the
value of '__STDC_VERSION__'.

(I won't be giving ANSI $232 for a PDF copy of ISO/IEC 9899:2018.)

David Brown

unread,
Feb 5, 2019, 4:40:06 PM2/5/19
to
On 05/02/2019 20:07, Bart wrote:
> On 05/02/2019 16:02, David Brown wrote:
>> On 05/02/2019 16:06, Bart wrote:
>
>
>> I can appreciate compilers not having C11 support - it is not a commonly
>> used standard.
>
> But if Chris used C11-only features that weren't supported by a
> compiler, what you be as dismissive of it as you are with the likes of
> MSVC?
>

Many C implementations are not yet fully C11 compatible - especially
threading (this is usually a library issue, not a compiler issue). This
is unfortunate. It is not too bad, however, in that C11 threads are not
likely to be particularly popular - they give you less than using
OS-specific threading libraries, or cross-platform threading libraries.
(It's different for C++11, which has a number of useful features in its
threading stuff.)

>
>> Again, look at the details.  People have been writing C99 code with gcc
>> for a very long time (though it did take a number of years before gcc's
>> C99 support was practically complete).
>
>
>>>> OK.  Call it "outdated compiler", or "toy compiler" if you prefer.
>>>> Don't call it a "C compiler", because that implies real, modern C.  (It
>>>> actually implies C17 support, since that is the current version of C.)
>>>
>>> So as soon as the first C17 compiler comes out, all other C compilers
>
> (I've just realised 2017 has come and gone! Are there any actual C17
> compilers?)
>

Since C17 is basically just a bug fix and a nicer typographical layout, yes.

>> No, I did not say anything like that.
>>
>>> Nonsense.
>
> You said this:
>
> "So 6 of them are not C99 compatible, and are not serious general
> purpose compiler implementations"
>
> Why C99? The current C standard is C11, which is now 8 years old. Yet
> you seem to consider one that doesn't support 100% C11 as still a
> serious contender, but one that doesn't support 100% C99 as not serious.
> Or a toy.

Basically, C99 came with a number of major and useful additions to the
language - few people who use C99 regularly would ever want to go back
to C90. It also has a large number of minor or obscure features, some
of which are optional, which may be of relevance to a few people but are
rarely needed (like support for systems with more restricted character
sets, and hexidecimal floating point formats). I don't see it as a big
problem if those are missing from a compiler that covers the major parts
of C99. But an implementation that can't handle "long long" in a
printf, type-generic maths, compound literals, designated initialisers,
VLA's, mixed declarations and code, inline, _Bool, and // comments -
that is just painful to use.

C11 added relatively little to the language - support for multi-threaded
code is the main point (whether or not you use its <thread.h>
functions), with atomics, _Generic and _Static_assert being other useful
points.

C17 is, as has been said, a bug fix. I'd expect any compiler that has
basic C11 in place, and is actively developed, to have C17 support
before long.

>
> That it took so many years to get widespread C99 support suggests to me
> that it wasn't easy to do. (I know you will argue every single C99
> feature is trivial to support.) And it took gcc apparently over a decade.
>

I assume that some of the C99 features were not trivial to implement.
Development of compilers takes time, as does their distribution into
mainstream use. There has been enough time for C99.

> So it does seem to me that you are being unduly harsh on any
> implementation that hasn't yet, or doesn't want to, support full C99. It
> didn't stop gcc from being used seriously during the years it took to
> achieve full compliance.

I probably have been unduly harsh - it's a fair criticism. But I would
not consider a C implementation to be a modern general-purpose tool
suitable for developing new C code unless it had at least the majority
of C99 support. It might be okay as a niche or special-purpose tool,
and might be okay for building existing code.

>
> These are some significant C99 features that my bcc project doesn't
> support (and won't do as I've lost interest):
>
>   - VLAs including variable types as parameters
>   - Designated initialisers
>   - Compound literals
>   - Complex types (might have been pre-C99)
>
> Interestingly, many of those aren't supported by C++ either, so I'm not
> going to lose any sleep over them.

C++ has complex types - but they are implemented as a template library,
rather than a fundamental type (that is the right choice for C++, and
was not an option for C - but it is unfortunate that the result is
incompatible).

C++ does not have VLA's as such, but some kinds of array (those with a
compile-time "const" size) are allowed as normal arrays in C++ while
they count as VLA's in C. In C++, std::vector is usually used where you
might have a VLA in C.

Designated initialisers are an extremely useful feature, and can make
code significantly more robust in the face of changes. (Imagine you
have a struct type declared in a header, and you want to initialise an
object of that type in your code. Designated initialisers make this
safe even when the struct definition changes.) It is something that has
long been called for in C++, and is part of the next C++20 standard.

I find compound literals to be occasionally neat. I don't use them much.



Chris M. Thomasson

unread,
Feb 7, 2019, 1:38:04 AM2/7/19
to
[...]
> bool
> ct_plot_add(
>     struct ct_plot* const self,
>     double complex z,
>     struct ct_rgb const* color
> ){
>     long x = (creal(z) - self->plane.axes.xmin) / self->plane.xstep;
>     long y = (self->plane.axes.ymax - cimag(z)) / self->plane.ystep;
>
>     if (x > -1 && x < (long)self->canvas->width &&
>         y > -1 && y < (long)self->canvas->height)
>     {
>         // Now, we can convert to index.
>         size_t i = x + y * self->canvas->width;
>
>         assert(i < self->canvas->height * self->canvas->width);
>
>         struct ct_rgb exist = self->canvas->buf[i];
>
>         if (exist.r + 1 != 0)
>         {
>              exist.r += 1;
>         }
>
>         self->canvas->buf[i] = exist;
>         return true;
>     }
>
>     return true;
> }
[...]
>


Ahh shi%, this function ct_plot_add needs to return false if it fails.
My current experimental program does not care about the return value,
but nonetheless it should still be correct. Notice how it is returning
true twice, for a success and failure condition? Not Kosher!

Luckily, this return condition is not used. Phew... ;^o

Chris M. Thomasson

unread,
Feb 11, 2019, 4:44:23 PM2/11/19
to
On 2/6/2019 10:37 PM, Chris M. Thomasson wrote:
> On 1/28/2019 10:20 PM, Chris M. Thomasson wrote:
>> On 1/25/2019 2:24 PM, Chris M. Thomasson wrote:
>>> Just wondering, when you get some free time, if you can perhaps run
>>> the my experimental plotter code that generates a PPM called
>>> "ct_plane.ppm". And if you can see a fractal in the resulting
>>> rendering. Here is my C99 code:
>>>
>>> https://pastebin.com/raw/322XAnsT
>>> (raw text, pure C99: no ads!)
>>>
>>> It should just compile with GCC, and run like a charm. It has proper
>>> aspect ratios so one can use any dimensions they want.
[...]

>>          self->canvas->buf[i] = exist;
>>          return true;
>>      }
>>
>>      return true;
>> }
> [...]
>>
>
>
> Ahh shi%, this function ct_plot_add needs to return false if it fails.
> My current experimental program does not care about the return value,
> but nonetheless it should still be correct. Notice how it is returning
> true twice, for a success and failure condition? Not Kosher!
>
> Luckily, this return condition is not used. Phew... ;^o

Fwiw, just remembering now, I actually used this return condition in my
original C++11 code. If ct_plot_add returns false, it means that the
point is off the screen, or visible plotting range if you will. Well, I
used a little loop and kept pulling the point into an origin point such
that it was able to be plotted within the viewport, or screen if you will.
0 new messages