1. Can printf() be made smarter?

Hey all, I'm a long time lurker and hobby programmer but new to posting.

I'm working on a program to read a text file, process the file and write results in a text file for another program to read. The input file, an AutoCad .dxf, contains a lot of integers and floats in text format with varying decimal places depending on the drawing details. Ideally my program would write some of these numbers in the output file with the same precision that they are read, i.e. with the same number of decimal places.

That's where the fun starts - how to get printf() to write the number of places the float contains.

Example: the number 12845.81. A harmless number read from the dxf, contained in an EU atom x. Let's write it in the output file (all in EU version 4.0, in case it matters):
printf(fn,"%f\n",x) returns 12845.810000

OK so let's try restricting the field width (yeah, I know it shouldn't work)
printf(fn,"%8f\n",x) returns 12845.810000

I know this works
printf(fn,"%.2f\n",x) returns 12845.81
but what if the number had 3 or more decimal places, which some in this file do?

So we give up on %f, which by default always shows 6 decimal places unless told otherwise, and try %g
printf(fn,"%g\n",x) returns 12845.8

What happened to the 1? %g seems to prefer 6 significant digits, even if that means losing significant decimal places. Why do I say this?
printf(fn,"%g\n",x) for x=128455.81 (note the bigger x) returns 128456 with no decimal places.

Lets 'encourage' it to include more digits (back to the original x):
printf(fn,"%8g\n",x) returns _12845.8 (where _ means space)

Incidentally, this is weird:
printf(fn,"%.3g\n",x) returns 1.28e+04.
How is that the most appropriate way to display this number?

'Encouraging' it to expand the number out results in:
printf(fn,"%9.3g\n",x) returns _1.28e+04 (again, _ means space)

I conclude %g isn't very useful, or at least not reliable.

What to do? printf() is an EU inbuilt function so I can't see what it does in an include file, so I went to the C source files. I chased it as far as I could, to C's snprintf() in stdio.h, but I'm not a C programmer so probably missed something. It seems EU simply passes the printf() arguments direct to C's snprintf(), plus the internet told me C's printf("%f") by default returns 6 decimal places. So C is the culprit and Euphoria doesn't do anything to change it.

I know I can fix it by, for example, s = sprintf("%.16f",x) then a loop to delete trailing zeros from s and puts(fn,s). I'll probably include that in a function in my program, messy as it is. However, two questions:

1. Is there a better way to fix this with current Euphoria? This isn't important as I already have a strategy that will work, however I'm interested to see what people come up with.

2. Can Euphoria be made 'smarter' to fix C's lack of smarts with both %f and %g, perhaps in future versions? In general I think of Euphoria as a smarter C, where it overcomes some of C's significant shortcomings, so why not with this admittedly very minor 'problem' as well? I don't think I'm good enough with C to do it myself, although I could just yell "YOLO" and dive right in.......

Cheers, Mark. (edited because arrows turn following text to strikeout - who knew?)

new topic     » topic index » view message » categorize

2. Re: Can printf() be made smarter?

MarkB said...

Hey all, I'm a long time lurker and hobby programmer but new to posting.

I'm working on a program to read a text file, process the file and write results in a text file for another program to read. The input file, an AutoCad .dxf, contains a lot of integers and floats in text format with varying decimal places depending on the drawing details. Ideally my program would write some of these numbers in the output file with the same precision that they are read, i.e. with the same number of decimal places.

...

Hi Mark,

1) If the input formats are the same as the output formats you could simply pass the values directly as text and not worry about text to atom to text conversions.

2) However, if you do need to process the values at all numerically and can't escape printf(), I suggest writing your own routines to take care of the different styles (if there aren't that many). You don't have to stick to the printf() conventions either:

sequence t = myprintf("This value is %10f2", A) --> This value is 0000000123.45 
sequence t = float10_2(A) --> 0000000123.45 
sequence t = myfloat(A, 10, 2) --> 0000000123.45 

Spock

new topic     » goto parent     » topic index » view message » categorize

3. Re: Can printf() be made smarter?

Hi Spock, thanks for your reply.

You're right, for this current program I could use value() to get and use the number as an atom and still pass the original text direct to the output file. In the near future, however, my program will need to add a correction to x and y so can't avoid printf() or equivalent.

Doesn't change my comments about the lack of smarts in printf(), though. Euphoria is all about not having to work around silly issues like this.

Cheers.

new topic     » goto parent     » topic index » view message » categorize

4. Re: Can printf() be made smarter?

MarkB said...

printf(fn,"%.2f\n",x) returns 12845.81
but what if the number had 3 or more decimal places, which some in this file do?

You could try something like fmt=sprintf("%%.%df\n",ndp); printf(fn,fmt,x).

MarkB said...

So we give up on %f, which by default always shows 6 decimal places unless told otherwise, and try %g
printf(fn,"%g\n",x) returns 12845.8

What happened to the 1?

There is a subtle but important difference between %f and %g:

The Phix manual said...

Field widths can be added to the basic formats, e.g. %5d, %8.2f, %10.4s. The number before the decimal point is the minimum field width to be used. The number after the decimal point is the precision to be used.

For %f and %e, the precision specifies how many digits follow the decimal point character, whereas for %g, the precision specifies how many significant digits to print...

Note that "%f" is effectively identical to "%.6f", and, albeit with that subtle difference in mind, "%g" is equivalent to "%.6g", I think.

MarkB said...

printf(fn,"%8g\n",x) returns _12845.8 (where _ means space)

"%.8g" fares better.

MarkB said...

Incidentally, this is weird:
printf(fn,"%.3g\n",x) returns 1.28e+04.
How is that the most appropriate way to display this number?

1.28e+04 == 12800, which is 12845.81 to 3 significant figures, jut like you asked. It gets its knickers in a bit of a twist because length("12800")>3, or something like that.

MarkB said...

I know I can fix it by, for example, s = sprintf("%.16f",x) then a loop to delete trailing zeros from s and puts(fn,s). I'll probably include that in a function in my program, messy as it is.

Yep, messy as it is, best way I ever found. Other options may include (OE) routines like display() perhaps, outside my ken.

MarkB said...

Can Euphoria be made 'smarter' to fix C's lack of smarts with both %f and %g, perhaps in future versions?

That would break (far too much) existing code.

If you are looking for a "pure-eu" version of printf(), dig out builtins\VM\pprntfN.e from https://bitbucket.org/petelomax/phix/src

It will now need a couple of tweaks (replace "#ilASM") to run on OE, but there should be all the compatible code needed right there, just commented out.

Pete

new topic     » goto parent     » topic index » view message » categorize

5. Re: Can printf() be made smarter?

Hi Pete, thanks for replying.

Yes, %.8g fares better, but I thought still requires knowledge of the significant digits in the source atom. However after reading your post I compared %.10g (result 12835.81) and %.10f (result 12835.8100000000) so I take back my original comment regarding %g being useless. It actually works and deletes the trailing zeros itself, why didn't I think to try this before?

Seems the lack of smarts wasn't in printf() after all.

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu