Download raw body.
exact floating point calculations in roff(7)
On 2025-04-03 08:09, Ingo Schwarze wrote:
> Hello John,
>
> On Wed, Apr 02, 2025 at 02:52:39PM -0400, j@bitminer.ca wrote:
>
>> Hello, I think you left out the best part:
>>
>> switch (unit) {
>> case 'f':
>> myres *= 65536.0;
>> break;
>> ...
>> <snip>
>> ...
>> case 'M':
>> myres *= 24.0 / 100.0;
>> break;
>> default:
>> break;
>> }
>> if (res != NULL)
>> *res = myres; <--- ***here***
>>
>> The whole point seems to be to calculate an integer,
>> returned in *res.
>
> That is completely correct.
> The point is that the roff(7) language allows users to specify
> arbitrary floating point numbers and these can be scaled with
> so-called scaling units - but the end result is indeed an integer.
>
>> Some programmer
>
> That was me. :-)
Ahh, not knowing the history here, I tried to be neutral.
No offense intended.
>
>> who might have been less inclined to write scaled
>> integer arithmetic code
>
> Before i wrote the double arithmetic code, that code did use integer
> arithmetic. I had written that integer arithmetic code several years
> earlier.
>
>> decided, instead, to adopt doubles.
>
> I deemed the change necessary because the roff(7) input syntax
> supports decimal fractions.
>
> Admittedly, it might be possible to parse 23.999 as *two* integers
> (23 and 999), scale both parts separately, do some kind of carry,
> and finally add both results, all using integer arithmetics.
> I admit i did not consider that possibility when switching this
> code to floating point, but it does not feel like a simple solution.
>
>> OK, fine, but I think an implied goal here is to generate an exact
>> *rounded* result. Which is necessary because as often seen doubles
>> don't calculate integer values very reliably.
>>
>> While your fix is obviously valid I think the fix could also have
>> been
>>
>> *res = trunc(myres + 0.5);
>>
>> which eliminates the 23.999999 problem in the usual floating-point
>> way.
>
> No, that would be incorrect behaviour.
> If myres is 23.9 or 23.99 or even 23.999999, then roff(7) semantics
> requires that *res must become 23, but your rounding would make it 24.
So now that makes me confused, as your original example was
> 100M = 100 * 24/100 = 23 # sic, 23 instead of 24
which result was correct/incorrect depending on compiler options. Is
it supposed to be (100*24)/100 or the likely broken 100*(24/100)? It
seems roff(7) is way outside my comfort zone after all.
Your test case attempt:
> double x, y;
> x = 100.0;
> y = 24.0/100.0;
> x *= y;
will not work -O2 as the compiler reduces the arithmetic to a
load/store of a compile-time computed constant.
I suggest inserting
void
altc(double *a, double *b) {
return;
}
as a separate compilation unit, and adding
void
altc( double *a, double *b);
near the top, and adding
altc(&x, &y);
before
x *= y;
which outsmarts the compiler and forces arithmetic even -O2.
As a suggestion, perhaps there should be specific comments
to explain the ordered and simple arithmetic statements.
cheers
J
exact floating point calculations in roff(7)